diff --git a/apps/ios/Info.plist b/apps/ios/Info.plist
index 4c4d8523c9c02c23057ad0e2cd42d2e08da38af4..ef1cb4094c7a988eb5bb7a1aeeeda6ca3b0f84ed 100644
--- a/apps/ios/Info.plist
+++ b/apps/ios/Info.plist
@@ -4,6 +4,21 @@
CADisableMinimumFrameDurationOnPhone
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeName
+ OneNote Section
+ CFBundleTypeRole
+ Editor
+ LSHandlerRank
+ Alternate
+ LSItemContentTypes
+
+ com.microsoft.onenote.section
+
+
+
LSSupportsOpeningDocumentsInPlace
NSCameraUsageDescription
@@ -38,5 +53,27 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ UTImportedTypeDeclarations
+
+
+ UTTypeConformsTo
+
+ public.data
+
+ UTTypeDescription
+ OneNote Section
+ UTTypeIdentifier
+ com.microsoft.onenote.section
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ one
+
+ public.mime-type
+ application/onenote
+
+
+
diff --git a/apps/ios/Snowbound/App.swift b/apps/ios/Snowbound/App.swift
index 37c8e5b2bf004626abf814304a791664146358b7..f9b5ac77699c548fc50618072c125d7093e55f84 100644
--- a/apps/ios/Snowbound/App.swift
+++ b/apps/ios/Snowbound/App.swift
@@ -19,6 +19,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
sb_set_coordinator(coordinate)
+ Editing.apply()
ICloud.start()
sb_set_sync_wake { DispatchQueue.main.async { NotificationCenter.default.post(name: Sync.changed, object: nil) } }
#if DEBUG
@@ -70,6 +71,63 @@ enum Appearance {
}
}
+/// OneNote's Snap To Grid and Default font, kept between launches, which the pages opened
+/// from then on take.
+enum Editing {
+ /// OneNote 2010's Default font sizes, in points.
+ static let sizes: [Float] = [8, 9, 9.5, 10, 10.5, 11, 11.5, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
+ static let faces = ["Calibri", "Arial", "Times New Roman", "Courier New", "Georgia", "Verdana"]
+
+ static var snapToGrid: Bool {
+ get { !UserDefaults.standard.bool(forKey: "ignoreGrid") }
+ set {
+ UserDefaults.standard.set(!newValue, forKey: "ignoreGrid")
+ apply()
+ }
+ }
+
+ static var face: String {
+ get { UserDefaults.standard.string(forKey: "defaultFontFace") ?? "Calibri" }
+ set {
+ UserDefaults.standard.set(newValue, forKey: "defaultFontFace")
+ apply()
+ }
+ }
+
+ static var size: Float {
+ get { UserDefaults.standard.object(forKey: "defaultFontSize") as? Float ?? 11 }
+ set {
+ UserDefaults.standard.set(newValue, forKey: "defaultFontSize")
+ apply()
+ }
+ }
+
+ static func apply() {
+ sb_set_snap_to_grid(snapToGrid)
+ sb_set_default_font(face, size)
+ }
+
+ /// Options' Default font: the face and size new text takes.
+ static func fontMenu() -> UIMenu {
+ let faces = faces.map { name in
+ let action = UIAction(title: name) { _ in face = name }
+ action.state = face == name ? .on : .off
+ return action
+ }
+ let sizes = sizes.map { points in
+ let action = UIAction(title: points.formatted()) { _ in size = points }
+ action.state = size == points ? .on : .off
+ return action
+ }
+ return UIMenu(
+ title: "Default Font", image: UIImage(systemName: "textformat"),
+ children: [
+ UIMenu(options: .displayInline, children: faces),
+ UIMenu(title: "Size", children: sizes),
+ ])
+ }
+}
+
/// Where the reader was, reopened on the next launch as Notes reopens its last note.
private struct Place: Codable {
let notebook: String
@@ -123,6 +181,12 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewContro
NotificationCenter.default.addObserver(forName: ICloud.changed, object: nil, queue: .main) { [weak self] _ in
self?.notebooks.rescan { self?.restore($0) }
}
+ self.scene(scene, openURLContexts: options.urlContexts)
+ }
+
+ /// A section file Files opens in Snowbound, in place.
+ func scene(_ scene: UIScene, openURLContexts contexts: Set) {
+ for context in contexts where context.url.isFileURL { notebooks.open(document: context.url) }
}
/// Reopens the section and page shown last, or the scripted one.
diff --git a/apps/ios/Snowbound/CanvasView.swift b/apps/ios/Snowbound/CanvasView.swift
index a28de9f73aa5d87e6203d72203bd6713a6ec8a3e..b2ed72e138d477a6cd0af10a4eba8e5cfde9c44a 100644
--- a/apps/ios/Snowbound/CanvasView.swift
+++ b/apps/ios/Snowbound/CanvasView.swift
@@ -193,6 +193,8 @@ final class CanvasView: UIScrollView, UIScrollViewDelegate, UITextInput, UITextI
object: nil)
registerForTraitChanges([UITraitUserInterfaceStyle.self]) { (view: CanvasView, _) in view.paper() }
formatBar.onApply = { [weak self] command in self?.apply(command) }
+ formatBar.onStyle = { [weak self] place in self?.applyStyle(place) }
+ formatBar.stylePlace = { [weak self] in self?.stylePlace ?? -1 }
formatBar.onDismiss = { [weak self] in _ = self?.resignFirstResponder() }
}
@@ -879,6 +881,18 @@ final class CanvasView: UIScrollView, UIScrollViewDelegate, UITextInput, UITextI
var pageText: String { handle.flatMap { take(sb_view_page_text($0)) } ?? "" }
+ /// The page on `paper`, in points, as OneNote 2010 prints it, each sheet's footer naming
+ /// `section`.
+ func pdf(paper: CGSize, section: String) -> Data? {
+ guard let handle else { return nil }
+ var length = 0
+ guard let bytes = sb_view_pdf(handle, Float(paper.width), Float(paper.height), section, &length) else {
+ return nil
+ }
+ defer { sb_bytes_free(bytes, length) }
+ return Data(bytes: bytes, count: length)
+ }
+
/// The selection's formatting, from `sb_view_format`.
var format: UInt64 { handle.map(sb_view_format) ?? 0 }
@@ -934,6 +948,15 @@ final class CanvasView: UIScrollView, UIScrollViewDelegate, UITextInput, UITextI
edit(external: true) { sb_view_apply(handle, command) }
}
+ /// Gives the selected paragraphs style `place` of OneNote's gallery, in the page's theme.
+ func applyStyle(_ place: UInt8) {
+ guard let handle else { return }
+ edit(external: true) { sb_view_style(handle, place) }
+ }
+
+ /// The selection's style's place in the gallery, or -1.
+ var stylePlace: Int32 { handle.map(sb_view_style_place) ?? -1 }
+
/// Shows the page as stored; with `discard`, after an edit was refused.
func reload(discard: Bool) {
guard let handle else { return }
diff --git a/apps/ios/Snowbound/FormatBar.swift b/apps/ios/Snowbound/FormatBar.swift
index 926c1da50065f09b965881b60a61b067396414e0..91f7980f369c39a0c1463863fc0f44862774222e 100644
--- a/apps/ios/Snowbound/FormatBar.swift
+++ b/apps/ios/Snowbound/FormatBar.swift
@@ -19,6 +19,14 @@ final class FormatBar: UIInputView {
private var toggles: [(UIButton, UInt8)] = []
private var bits: UInt64 = 0
var onApply: ((UInt8) -> Void)?
+ /// Applies a style of OneNote's gallery, by place; `stylePlace` names the selection's.
+ var onStyle: ((UInt8) -> Void)?
+ var stylePlace: (() -> Int32)?
+ /// OneNote 2010's Styles gallery, in its order.
+ private static let styles = [
+ "Heading 1", "Heading 2", "Heading 3", "Heading 4", "Heading 5", "Heading 6",
+ "Page Title", "Citation", "Quote", "Code", "Normal",
+ ]
/// Asks for a picture from the camera (true) or the photo library.
var onPicture: ((Bool) -> Void)?
var onDismiss: (() -> Void)?
@@ -33,6 +41,19 @@ final class FormatBar: UIInputView {
addSubview(backdrop)
let row = UIStackView()
row.spacing = 2
+ let styles = Self.button("textformat", "Styles")
+ styles.menu = UIMenu(children: [
+ UIDeferredMenuElement.uncached { [weak self] done in
+ let current = self?.stylePlace?() ?? -1
+ done(Self.styles.enumerated().map { place, name in
+ UIAction(title: name, state: Int32(place) == current ? .on : .off) { _ in
+ self?.onStyle?(UInt8(place))
+ }
+ })
+ }
+ ])
+ styles.showsMenuAsPrimaryAction = true
+ row.addArrangedSubview(styles)
for (symbol, label, command) in Self.buttons {
let button = Self.button(symbol, label)
button.addAction(UIAction { [weak self] _ in self?.onApply?(command) }, for: .primaryActionTriggered)
diff --git a/apps/ios/Snowbound/ICloud.swift b/apps/ios/Snowbound/ICloud.swift
index 233bfdd1fea102b542cb6dbcfa0727b038604993..62fdc27a8fb43e443df4ebefd25ca87cc3e2f802 100644
--- a/apps/ios/Snowbound/ICloud.swift
+++ b/apps/ios/Snowbound/ICloud.swift
@@ -54,15 +54,27 @@ enum ICloud {
/// Whether iCloud is signed in with iCloud Drive on, container or not.
static var signedIn: Bool { FileManager.default.ubiquityIdentityToken != nil }
- /// Has the library merge conflict versions, and follows the account; call once at launch.
+ /// Has the library merge conflict versions, follows the account, and hears of notebooks
+ /// another device adds or removes; call once at launch.
static func start() {
sb_set_versions(listVersions, retireVersion)
NotificationCenter.default.addObserver(
forName: .NSUbiquityIdentityDidChange, object: nil, queue: .main
) { _ in lookUp() }
lookUp()
+ query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope]
+ query.predicate = NSPredicate(format: "%K LIKE '*'", NSMetadataItemFSNameKey)
+ for name in [Notification.Name.NSMetadataQueryDidFinishGathering, .NSMetadataQueryDidUpdate] {
+ NotificationCenter.default.addObserver(forName: name, object: query, queue: .main) { _ in
+ if Notebooks.inCloudChanged { NotificationCenter.default.post(name: changed, object: nil) }
+ }
+ }
+ query.start()
}
+ /// Reports what iCloud Drive syncs of the container, as files come from other devices.
+ private static let query = NSMetadataQuery()
+
/// Looks the folder up off the main thread, as asking iCloud can take a while.
static func lookUp() {
background({ () -> URL? in
@@ -77,4 +89,5 @@ enum ICloud {
NotificationCenter.default.post(name: changed, object: nil)
}
}
+
}
diff --git a/apps/ios/Snowbound/Ink.swift b/apps/ios/Snowbound/Ink.swift
index af8746276b230e51c7e1e506d80c83aa8f72f023..c7c1ca1fb3db21a5beb9c5d2b18efe0d1486aac7 100644
--- a/apps/ios/Snowbound/Ink.swift
+++ b/apps/ios/Snowbound/Ink.swift
@@ -240,8 +240,9 @@ final class InkPicker: UIView {
color.accessibilityLabel = "Colour, \(Self.name(shown.color))"
}
+ /// The shapes, then OneNote's Snap To Grid.
private func shapeMenu() -> [UIMenuElement] {
- Self.shapes.enumerated().map { place, shape in
+ let shapes = Self.shapes.enumerated().map { place, shape in
let kind = UInt8(place)
let action = UIAction(title: shape.1, image: UIImage(systemName: shape.0)) { [weak self] _ in
self?.onPick?(.shape(kind))
@@ -249,6 +250,11 @@ final class InkPicker: UIView {
action.state = tool == .shape(kind) ? .on : .off
return action
}
+ let snap = UIAction(title: "Snap To Grid", image: UIImage(systemName: "grid")) { _ in
+ Editing.snapToGrid.toggle()
+ }
+ snap.state = Editing.snapToGrid ? .on : .off
+ return [UIMenu(options: .displayInline, children: shapes), UIMenu(options: .displayInline, children: [snap])]
}
/// The colours of the kind of pen shown, and for a pen its two widths.
diff --git a/apps/ios/Snowbound/Lists.swift b/apps/ios/Snowbound/Lists.swift
index 0c31c065fbd92eeb58915cf5544f1bb615db1245..ce578f1d86747a218eeb30943494deb8173a1249 100644
--- a/apps/ios/Snowbound/Lists.swift
+++ b/apps/ios/Snowbound/Lists.swift
@@ -104,6 +104,7 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke
Author.ask(from: self) {}
})
more.append(Appearance.menu())
+ more.append(Editing.fontMenu())
provide(actions + [UIMenu(options: .displayInline, children: more)])
}
])
@@ -535,10 +536,13 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke
private func add(_ notebook: Notebook) {
Notebooks.add(notebook)
+ openFirstSection(of: notebook)
+ }
+
+ private func openFirstSection(of notebook: Notebook) {
reload()
notebook.open { [weak self] in
self?.reload()
- // A notebook just added opens at its first section.
if let tab = notebook.tabs.first(where: \.readable) { self?.onOpen?(tab, notebook) }
}
}
@@ -549,14 +553,21 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke
guard let author = Author.name else {
return Author.ask(from: self) { [weak self] in self?.newNotebook(inCloud: inCloud) }
}
- let alert = UIAlertController(title: "New Notebook", message: nil, preferredStyle: .alert)
+ let alert = UIAlertController(
+ title: inCloud ? "New iCloud Notebook" : "New Notebook", message: nil, preferredStyle: .alert)
let create = UIAlertAction(title: "Create", style: .default) { [weak self, weak alert] _ in
let name = alert?.textFields?.first?.text?.trimmingCharacters(in: .whitespaces) ?? ""
self?.create(name, by: author, inCloud: inCloud)
}
- create.isEnabled = false
alert.addTextField { field in
field.placeholder = "Name"
+ // A notebook in iCloud Drive starts named for it, so the list tells it apart.
+ if inCloud {
+ let taken = Set(Notebooks.inCloud.map(\.name))
+ field.text = (1...).lazy.map { $0 == 1 ? "iCloud Notebook" : "iCloud Notebook \($0)" }
+ .first { !taken.contains($0) }
+ }
+ create.isEnabled = field.text?.isEmpty == false
field.autocapitalizationType = .words
NotificationCenter.default.addObserver(
forName: UITextField.textDidChangeNotification, object: field, queue: .main
@@ -634,9 +645,25 @@ final class NotebooksViewController: UICollectionViewController, UIDocumentPicke
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
- guard let url = urls.first else { return }
+ if let url = urls.first { open(document: url) }
+ }
+
+ /// Opens the folder or section file at `url`, chosen here or opened from Files, at its first
+ /// section, listing it unless it is listed.
+ func open(document url: URL) {
let accessing = url.startAccessingSecurityScopedResource()
defer { if accessing { url.stopAccessingSecurityScopedResource() } }
+ let listed = Notebooks.all.first { notebook in
+ guard case .files(let bookmark) = notebook.source else { return false }
+ var stale = false
+ let location = try? URL(resolvingBookmarkData: bookmark, bookmarkDataIsStale: &stale)
+ return location?.standardizedFileURL == url.standardizedFileURL
+ }
+ if let listed {
+ // A notebook still opening at launch lists no sections yet.
+ if let tab = listed.tabs.first(where: \.readable) { onOpen?(tab, listed) } else { openFirstSection(of: listed) }
+ return
+ }
guard let bookmark = try? url.bookmarkData() else { return }
add(Notebook(name: url.deletingPathExtension().lastPathComponent, source: .files(bookmark: bookmark)))
}
diff --git a/apps/ios/Snowbound/Notebooks.swift b/apps/ios/Snowbound/Notebooks.swift
index b408549b0a0daad3010304af9144075cc7fddd5a..17562e7fdfb14a1e7ff3021420d38e255db6cb2a 100644
--- a/apps/ios/Snowbound/Notebooks.swift
+++ b/apps/ios/Snowbound/Notebooks.swift
@@ -277,7 +277,8 @@ final class Notebook {
}
/// Asks iCloud Drive for the notebook files it keeps elsewhere, which iOS lists as
- /// `.Name.icloud`, and lists the sections again until they arrive.
+ /// `.Name.icloud` or dataless under their names, and lists the sections again until they
+ /// arrive.
private func download() {
let folder: URL? =
switch source {
@@ -288,13 +289,17 @@ final class Notebook {
case .server: nil
}
guard let folder, recheck == nil,
- let files = FileManager.default.enumerator(at: folder, includingPropertiesForKeys: nil)
+ let files = FileManager.default.enumerator(
+ at: folder, includingPropertiesForKeys: [.ubiquitousItemDownloadingStatusKey])
else { return }
var waiting = false
- for case let stub as URL in files where stub.lastPathComponent.hasPrefix(".") && stub.pathExtension == "icloud" {
- let name = String(stub.deletingPathExtension().lastPathComponent.dropFirst())
- let file = stub.deletingLastPathComponent().appendingPathComponent(name)
- try? FileManager.default.startDownloadingUbiquitousItem(at: file)
+ for case let file as URL in files {
+ let stub = file.lastPathComponent.hasPrefix(".") && file.pathExtension == "icloud"
+ let status = try? file.resourceValues(forKeys: [.ubiquitousItemDownloadingStatusKey])
+ guard stub || status?.ubiquitousItemDownloadingStatus == .notDownloaded else { continue }
+ let real = file.deletingLastPathComponent().appendingPathComponent(
+ String(file.deletingPathExtension().lastPathComponent.dropFirst()))
+ try? FileManager.default.startDownloadingUbiquitousItem(at: stub ? real : file)
waiting = true
}
guard waiting else { return }
@@ -485,6 +490,13 @@ enum Notebooks {
return added
}
+ /// Whether Snowbound's folder in iCloud Drive holds other notebooks than the list shows, as
+ /// when another device added or removed one.
+ static var inCloudChanged: Bool {
+ guard let folder = ICloud.documents, scripted == nil else { return false }
+ return notebooks(in: folder).map { "iCloud/" + $0 } != inCloud.map(\.id)
+ }
+
/// The notebook folders and section files in `folder`, by name, as Files orders them.
private static func notebooks(in folder: URL) -> [String] {
let listed = try? FileManager.default.contentsOfDirectory(
@@ -648,6 +660,27 @@ final class Section {
}
}
+ /// The notebook's themes, and the one page `id`, its section and its notebook each name.
+ struct Themes: Decodable {
+ struct Listed: Decodable {
+ let id: String
+ let name: String
+ }
+ let themes: [Listed]
+ let page: String?
+ let section: String?
+ let notebook: String?
+ }
+
+ func themes(of id: String) -> Themes? { decode(Themes.self, sb_section_themes(handle, id)) }
+
+ /// Gives `theme`, or none of its own, to page `id` (scope 0), its section (1) or its
+ /// notebook (2).
+ func setTheme(_ theme: String?, scope: UInt8, of id: String, done: @escaping (Bool) -> Void) {
+ let pointer = Int(bitPattern: handle)
+ background({ sb_section_set_theme(OpaquePointer(bitPattern: pointer), id, scope, theme) }, done: done)
+ }
+
/// Stores every edit and publishes them within `seconds`; call off the main thread.
func flush(_ seconds: Double) -> Bool { sb_section_flush(handle, seconds) }
diff --git a/apps/ios/Snowbound/PageViewController.swift b/apps/ios/Snowbound/PageViewController.swift
index 937623ef55f5be22250b987a417530218da2e5c2..ccaa9a8eb997ef3b8ac0d4b3aab941754e4d56d1 100644
--- a/apps/ios/Snowbound/PageViewController.swift
+++ b/apps/ios/Snowbound/PageViewController.swift
@@ -169,10 +169,19 @@ final class PageViewController: UIViewController, PHPickerViewControllerDelegate
UIAction(title: "Page Background…", image: UIImage(systemName: "paintpalette"), attributes: editable) {
[weak self] _ in self?.showPaper()
},
+ themeMenu(),
UIAction(title: "Tags Summary", image: UIImage(systemName: "tag")) { [weak self] _ in
guard let self else { return }
scene?.showTags(of: section.notebook, from: self)
},
+ UIMenu(options: .displayInline, children: [
+ UIAction(title: "Print…", image: UIImage(systemName: "printer")) { [weak self] _ in
+ self?.printPage(export: false)
+ },
+ UIAction(title: "Export as PDF…", image: UIImage(systemName: "doc.richtext")) { [weak self] _ in
+ self?.printPage(export: true)
+ },
+ ]),
UIMenu(options: .displayInline, children: [
UIAction(title: "New Subpage", image: UIImage(systemName: "text.badge.plus")) { _ in
scene?.newPage(subpage: true)
@@ -186,6 +195,69 @@ final class PageViewController: UIViewController, PHPickerViewControllerDelegate
]
}
+ /// The page's theme, its section's and its notebook's, each pickable; the page wears a
+ /// new one when it opens again.
+ private func themeMenu() -> UIMenu {
+ guard let themes = section.themes(of: page) else {
+ return UIMenu(title: "Theme", image: UIImage(systemName: "textformat"), children: [])
+ }
+ let scopes: [(String, String?)] = [
+ ("This Page", themes.page), ("This Section", themes.section), ("This Notebook", themes.notebook),
+ ]
+ let page = page
+ let menus = scopes.enumerated().map { scope, entry in
+ let (title, current) = entry
+ let choices = [(nil as String?, "None")] + themes.themes.map { ($0.id, $0.name) }
+ return UIMenu(title: title, children: choices.map { id, name in
+ UIAction(title: name, state: id == current ? .on : .off) { [weak self] _ in
+ guard let self else { return }
+ section.setTheme(id, scope: UInt8(scope), of: page) { [weak self] _ in
+ self?.onOpen?(page)
+ }
+ }
+ })
+ }
+ return UIMenu(title: "Theme", image: UIImage(systemName: "textformat"), children: menus)
+ }
+
+ /// Prints the page through the system's print sheet, or with `export` offers its PDF in
+ /// the share sheet, laid out as OneNote 2010 prints it on the region's paper.
+ private func printPage(export: Bool) {
+ let letter = ["US", "CA", "MX", "PR", "PH", "CL", "CO", "VE", "GT", "CR", "PA", "DO", "SV", "NI", "HN", "BZ"]
+ .contains(Locale.current.region?.identifier ?? "US")
+ let paper = letter ? CGSize(width: 612, height: 792) : CGSize(width: 595.276, height: 841.89)
+ let title = canvas.pageTitle ?? section.row(of: page)?.row.title ?? ""
+ guard let pdf = canvas.pdf(paper: paper, section: section.tab.name) else {
+ let alert = UIAlertController(
+ title: export ? "Couldn't Export the PDF" : "Couldn't Print",
+ message: "The page could not be laid out on paper.", preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: "OK", style: .default))
+ present(alert, animated: true)
+ return
+ }
+ if export {
+ let name = title.components(separatedBy: CharacterSet(charactersIn: "/\\:")).joined(separator: "-")
+ .trimmingCharacters(in: .whitespaces)
+ let file = FileManager.default.temporaryDirectory
+ .appendingPathComponent((name.isEmpty ? "Untitled" : name) + ".pdf")
+ do {
+ try pdf.write(to: file)
+ } catch {
+ return
+ }
+ let share = UIActivityViewController(activityItems: [file], applicationActivities: nil)
+ share.popoverPresentationController?.barButtonItem = more
+ present(share, animated: true)
+ } else {
+ let info = UIPrintInfo.printInfo()
+ info.jobName = title
+ let printer = UIPrintInteractionController.shared
+ printer.printInfo = info
+ printer.printingItem = pdf
+ printer.present(from: more, animated: true)
+ }
+ }
+
private func showPaper() {
let navigation = UINavigationController(rootViewController: PaperViewController(canvas: canvas))
navigation.sheetPresentationController?.detents = [.medium(), .large()]
diff --git a/arc/canvas.md b/arc/canvas.md
index 433dd51f6adeeb3ca4f8ca0f18a3459f6ad7313c..e098a9305527713283a691dd63ea2d4c5ef2e99b 100644
--- a/arc/canvas.md
+++ b/arc/canvas.md
@@ -48,6 +48,17 @@ ops still reach storage as it is typed. Undoing a deletion
brings back the original identities, so internal links to those paragraphs
survive an undo.
+Each page keeps its history while the window is open, as in OneNote 2010: the
+desktop app parks a page's editor when it is left and takes it up again, through
+the same reload in place, when the page opens. Between those edits the app keeps
+what was done to pages and sections (new, deleted, moved and indented pages, page
+and section names, sections moved), and Undo takes back whichever came last: the
+open page's own step, or the last such action in its section or notebook,
+showing the page it changes. Unlike OneNote, an edit doesn't end what actions Undo
+can reach, so New Page, a typed title, Undo, Undo takes the title and then the
+page. Each step back is computed against the section as it is then, one edit of
+its own; a step whose page is gone, or holds work it didn't make, is passed over.
+
Input-method composition (marked text) stays inside the editor until it
commits, and only then becomes ops. When another client changes the open page,
the editor compares the stored page with what it last read plus the ops it has
@@ -69,7 +80,8 @@ same line heights, same places. That took measuring, not guessing.
New Roman or Courier New, bundled metric-compatible substitutes (Carlito,
Arimo, Tinos, Cousine) stand in, under the stored font name.
- **OneNote's constants are OneNote's.** New outlines take OneNote's default
- width. Dragging snaps to its grid, anchored at the page's margin origin. Tags
+ width. Dragging snaps to its grid, anchored at the page's margin origin, unless
+ Snap To Grid (the shape gallery's last item, kept between launches) is off. Tags
sit in a column to the left of the text with OneNote's spacing, and a tag's
colour paints the whole paragraph as it does there.
@@ -106,6 +118,12 @@ on the page itself, as OneNote 2010 places it: the same column at the caret's
grid point, outside any outline. It selects, drags on the grid, deletes and
moves with Insert Space as a picture does, and opens and saves as above.
+A tag on a picture or file is stored on the object itself, as OneNote 2010 stores
+the tag Ctrl+1 gives a selected one, and drawn in a column left of it, centred on
+it; a click checks its box (`corpus/object-tags`). A picture's link follows on
+Ctrl+click, Command+click on macOS, while a click selects it, as OneNote's
+tooltip says (`corpus/picture-link`).
+
An outline holding only pictures or files takes a paragraph after them where a
click beside them lands, as OneNote 2010 adds one when typing there. It is
stored with the first edit that reaches it, and undoing back to it empty takes it
@@ -114,16 +132,20 @@ out again (`corpus/object-outline`).
A file printout's pages are pictures whose stored data is the printout's XPS
package. The page draws the PNG OneNote rendered of each
(WebPictureContainer14), framed in grey as OneNote frames them, and moves them as
-pictures. Snowbound does not write XPS, so a printout page deleted and brought
-back by undo returns as that picture (`corpus/printout`).
+pictures. A printout page deleted and brought back by undo returns as the page it
+was: its XPS package, the PNG and the properties tying it to the printout, written
+back as read (`corpus/printout`).
Pictures go in as OneNote 2010 pastes and inserts them. At a caret in text the
paragraph splits around the picture, as around a file. On blank page the picture
-lies on the page at the caret, and the caret moves to the grid row below it. A
-picture takes the size its resolution gives it, or 96 dpi without one. Paste
-takes files first (a picture file as its picture), then text, then a picture:
-Finder offers a copied file's name and icon beside it, and other apps a picture
-of copied text.
+lies on the page at the caret, and the caret moves to the grid row below it.
+From the title it joins the outline where the body starts, or lies two grid rows
+below the page's content when none starts there. A picture takes the size its
+resolution gives it, or 96 dpi without one. Paste takes files first (a picture
+file as its picture, as a dropped one goes in), then a web page holding
+pictures, its text and pictures in order as one undo step, then text, then a
+picture: Finder offers a copied file's name and icon beside it, and other apps a
+picture of copied text.
## Recordings
@@ -176,6 +198,14 @@ tablet pressure on Linux.
## Tables and selections across them
+Columns size as OneNote 2010 sizes them (`corpus/table-widths`). An unlocked
+column fits its widest line plus 4.347 pt, never under a new column's 37.11 pt,
+widening and narrowing with each edit, which stores the width in the same
+revision as its text. A table stops at the outline's width and its cells wrap
+from there. Dragging a column's right border resizes that column alone, the
+columns after it moving with it, down to 37.11 pt; on release it is one edit
+and one undo step, and the column is locked, so typing no longer fits it.
+
Note tags on a table sit in the tag column centred on it, and a click checks its
box (`corpus/table-tags`). A selection crossing a table's edge deletes as
OneNote 2010 deletes one: nothing joins across the edge, cells inside are
@@ -185,6 +215,28 @@ table with them when all do. What replaces the selection goes in at its start
Shift+Enter a caret inside a link, as OneNote's do; Alt+= across paragraphs
makes each paragraph's part an equation, and a deletion between two equations
joins them where neither seam lies inside an object (`corpus/equation-join`).
+An edit replacing a selection with an end inside an equation's object (a
+fraction, a script, a root) takes the whole object first, as OneNote's equation
+editor selects, and across paragraphs an object so taken at its paragraph's end
+takes that end too; a placeholder ("Type equation here.") is taken whole
+(`corpus/equation-select`).
+
+## Styles and themes
+
+The Styles gallery is OneNote 2010's: Heading 1 to 6, Page Title, Citation, Quote, Code
+and Normal, stored under OneNote's names (`h1`, `PageTitle`, `cite`, `blockquote`, `code`,
+`p`). Applying one gives the paragraph the page's style object of that definition and
+clears its character formatting but links, fields and language, as OneNote's does; Enter
+at a paragraph's end takes its style's NextStyle, so a heading is followed by Normal.
+Ctrl+Alt+1 to 6 apply the headings, and Clear Formatting at a caret applies Normal.
+
+A theme gives the eleven styles a look, and a page wears it as its style objects: OneNote
+draws a page by its own style objects, so OneNote users see the theme under the same
+names. Changing a theme moves each styled paragraph to a new style object of the same name
+(style objects are read-only) in one revision per page, and a page is brought to its
+theme when it opens, so a notebook-wide change costs each page only when someone looks at
+it. Which theme a notebook, section or page wears lives in the notebook's `.snowbound`
+folder, beside the tags' art (`corpus/styles`).
## Content the editor doesn't understand
@@ -240,13 +292,17 @@ template art print across the paper; the page colour does not. The footer names
section and numbers the sheets. `draw::pdf` writes the same primitives the screen paints
as PDF: text in subset fonts whose ToUnicode maps come from the laid-out text, so it
selects and searches, ligatures included; ink and shapes as paths; pictures and tag
-icons as images. The desktop's Print hands that PDF to AppKit's print panel, the XDG print
-portal or the shell's print verb for PDFs; Export as PDF saves it.
+icons as images. On the desktop, Print and Export as PDF first show OneNote's Print Preview
+and Settings, less the preview: the range (page, page group, section, and for a PDF the
+notebook), paper, orientation, fitting to the paper's width and the footer. Print then hands
+the PDF to AppKit's print panel, the XDG print portal or the shell's print verb for PDFs.
+On iOS the page menu prints the page through the print sheet or shares its PDF.
## Search, dates, conflicts
Smaller modules follow the same pattern of reproducing OneNote's behaviour
precisely. `search` matches the way OneNote 2010 searches: word prefixes,
-ignoring case and diacritics, title matches first. `date` edits the title's
+ignoring case and diacritics, title matches first, and the text OneNote recognised
+in pictures, which it stores in them, as OneNote's search finds it. `date` edits the title's
date and time fields the way OneNote stores a changed page date. `conflict`
shows conflict pages with OneNote's highlight.
diff --git a/arc/sync.md b/arc/sync.md
index 68e367b55c7311f0afcc1ee5d3250bdbde4cb65b..4fe269ae6f0309f53038f622a1f910c803be271b 100644
--- a/arc/sync.md
+++ b/arc/sync.md
@@ -285,8 +285,13 @@ version at once; nothing waits for the device that wrote it. A version of anothe
one that cannot be read, is kept beside the file as `Name (Device).one` instead.
On a cloud drive every read and write of a section file runs under `NSFileCoordinator`, so the
-iCloud daemon never swaps a file between the stamp check and the append. A file iCloud evicted is
-asked for and read later; the replica serves it meanwhile. Each publication is an upload and a
+iCloud daemon never swaps a file between the stamp check and the append. A file iCloud evicted
+(a `.Name.icloud` placeholder, or on macOS 14 and later a dataless file under its own name) lists
+as last read, or as downloading; the host asks for it, and reads the notebook again as files
+arrive. The sync thread never waits for one: the replica serves it meanwhile. Opening a section
+with no replica yet waits for its file. macOS and iOS offer no public way to keep a file
+downloaded, as the Finder's Keep Downloaded does; evicted files are asked for again whenever
+seen. Each publication is an upload and a
chance to conflict, so edits wait for a 3 second pause in typing (at most 30 seconds,
`Section::set_pause`), and publish at once when the app leaves the foreground, the screen locks,
the Mac's window loses focus or the app quits. A folder presenter reports other devices'
@@ -305,7 +310,10 @@ which are revision stores edited through the same transaction path. Moving or
renaming a file also rewrites two header fields OneNote checks on open (the
parent TOC's identity and a CRC of the file's name). Without them OneNote
treats the file as a stranger and re-identifies it. Deleting sends sections and
-pages to `OneNote_RecycleBin`, as OneNote does.
+pages to `OneNote_RecycleBin`, as OneNote does, and opening a notebook empties
+what the bin has held unchanged for 60 days, as OneNote prunes it: judged by the
+content's last change, the pages in one revision, binned section files deleted
+with their TOC entries left (`corpus/recycle-purge`).
What only Snowbound reads lives in the notebook's `.snowbound` folder, which carries the
Windows hidden attribute so that OneNote never makes a section group of it. Its files are
diff --git a/arc/ui.md b/arc/ui.md
index 0003b469f76087a3996f7a1d86046b92de3a68d8..2823a0eb21e759c3ceb7637b07d01cb76d917422 100644
--- a/arc/ui.md
+++ b/arc/ui.md
@@ -13,7 +13,8 @@ with a faint highlight along their tops. The open tab lies on top, in its
section's colour, and frames the page. That colour shades gently down the
window and becomes the accent for the whole interface, easing to the next
section's colour when you switch. Page tabs sit on the right, and the open
-one joins the page with rounded inside corners. It is OneNote 2010's layout,
+one joins the page with rounded inside corners; OneNote's Display options move
+them to the left and the notebooks to the right. It is OneNote 2010's layout,
drawn with soft shadows, concentric corner radii and a dark appearance. Call
it half-skeuomorphic: the shapes that made OneNote's notebook metaphor
legible, without the 2010 chrome.
@@ -81,7 +82,9 @@ frame N
*folds* its groups, boxes with a full and a folded form (`Spec::fold`), by
priority. Only then do boxes give up room by their *strictness*, the least
strict first. That one knob covers most of what flexbox is usually needed
- for.
+ for. A box filling across a parent sized by its children stretches to what
+ its siblings make it, and a popup that isn't strict gives way to the window,
+ so a dialog is as tall as its contents up to the window and scrolls within.
- **The toolbar is one row that never overflows.** Both forms of every group
are built each frame and the solver picks, so a group folds in the frame the
window narrows, with no widths remembered or worked out by the app. A folded
diff --git a/corpus/default-font/README.md b/corpus/default-font/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..54e35ad0de0d00cf60df7a8a2b73861480583ef7
--- /dev/null
+++ b/corpus/default-font/README.md
@@ -0,0 +1,21 @@
+# Default font
+
+OneNote 2010 keeps Options > General > Default font (Font, Size, Font color) in
+`HKCU\Software\Microsoft\Office\14.0\OneNote\Options\Editing` as `DefaultFontFace`,
+`DefaultFontSize` and `DefaultFontColor` (a COLORREF DWORD; its
+picker is Office's theme and standard colours, `options-font-color.png`). Observed in a lab clone on 2026-09-30, it never writes run
+formatting for it: a new page's `PageTitle` quick style takes the face at 17 pt
+whatever the size, and new text takes a `p` quick style in the face and size
+(plain, in the font colour, no highlight or paragraph spacing). On a page whose `p` is in
+another font, new text gets a second `p` beside it, the old text keeping its own.
+`native/Lab.one` is that section: "Title text" (Georgia 14), "T8" (Arial 8),
+"C14B14" (Calibri 14), then "Later" typed on "Title text" with Calibri 14, its
+second `p`. `native/Color.one` is a page made after picking Blue: `PageTitle` and `p`
+both take `#0070C0`, runs no colour of their own (`color.png`).
+
+`candidate/` is `crates/canvas/tests/default_font.rs`'s section, exported with
+`CANVAS_DEFAULT_FONT_EXPORT`: a page created titled in Georgia and Blue, and two outlines
+the editor started with the Default font Georgia 14 in Blue, each its own appended
+revision. `cold/page.xml` and `cold/page.png` are a fresh OneNote 2010's read:
+`PageTitle` Georgia 17 and one `p` Georgia 14, both `#0070C0`, as OneNote writes them.
+`tools/test_default_font.py` checks it without a VM.
diff --git a/corpus/default-font/candidate/Open Notebook.onetoc2 b/corpus/default-font/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..aa3bb97723efaa5542fa31f7bd3491638f6d83b0
Binary files /dev/null and b/corpus/default-font/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/default-font/candidate/fonts.one b/corpus/default-font/candidate/fonts.one
new file mode 100644
index 0000000000000000000000000000000000000000..072c1a176c0755b01bf05cf37f24462b4905538a
Binary files /dev/null and b/corpus/default-font/candidate/fonts.one differ
diff --git a/corpus/default-font/cold/page.png b/corpus/default-font/cold/page.png
new file mode 100644
index 0000000000000000000000000000000000000000..e46132effc35d3d901838f10d550849030571621
Binary files /dev/null and b/corpus/default-font/cold/page.png differ
diff --git a/corpus/default-font/cold/page.xml b/corpus/default-font/cold/page.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ee9cae629d3a14230733d11859c6a7f4c0fa85c3
--- /dev/null
+++ b/corpus/default-font/cold/page.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/default-font/native/Color.one b/corpus/default-font/native/Color.one
new file mode 100644
index 0000000000000000000000000000000000000000..7cd33a5c64b51cd06b8fc62c8997e339b65f50b1
Binary files /dev/null and b/corpus/default-font/native/Color.one differ
diff --git a/corpus/default-font/native/Lab.one b/corpus/default-font/native/Lab.one
new file mode 100644
index 0000000000000000000000000000000000000000..3669adb88d8aff85e1bd3fc5c64a0140f361060e
Binary files /dev/null and b/corpus/default-font/native/Lab.one differ
diff --git a/corpus/default-font/native/color.png b/corpus/default-font/native/color.png
new file mode 100644
index 0000000000000000000000000000000000000000..f9360edfaa65a2f0ed56e3acf8f351f21254bb83
Binary files /dev/null and b/corpus/default-font/native/color.png differ
diff --git a/corpus/default-font/native/options-font-color.png b/corpus/default-font/native/options-font-color.png
new file mode 100644
index 0000000000000000000000000000000000000000..52b1c8d0d84c085512760fb0f127ec43ea2993e6
Binary files /dev/null and b/corpus/default-font/native/options-font-color.png differ
diff --git a/corpus/object-tags/README.md b/corpus/object-tags/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..10300918137de05d850598bd66ec3b49cb944511
--- /dev/null
+++ b/corpus/object-tags/README.md
@@ -0,0 +1,27 @@
+# Tags on pictures and files
+
+`native/` is a notebook OneNote 2010 made in the lab: `tools/native/picture-objects.ps1`
+put a picture and a file in an outline, and a picture, a file and a picture of text on the
+page; each picture and file was then selected and tagged To Do with Ctrl+1 by hand.
+OneNote stores each tag on the picture or file itself (NoteTagStates on jcidImageNode and
+jcidEmbeddedFileNode), in an outline as on the page, though its COM read shows an outline
+object's tag on its paragraph. It draws the check box in a column left of the object,
+centred on it: 20.25 points out from an outline's picture, as from text, and 24.75 points
+from one on the page. It recognised "GLACIER HARBOR / Invoice 4471" in the picture of text
+and stored it as the picture's RichEditTextUnicode, which its search finds (the read's
+`OCRText`). A Ctrl+K on a selected picture there opened no dialog; the address typed
+became a link to a new page, which the section keeps.
+
+`candidate/` is the output of `tags_on_pictures_and_files_draw_and_check` in
+`crates/canvas/tests/pictures.rs`: the page picture's, the page file's and the outline
+picture's check boxes clicked, one revision each. `cold/` is a fresh OneNote 2010 read of
+it: those three checked, the outline file's not, and the recognised text kept.
+`tools/test_object_tags.py` checks this without a VM. Regenerate with
+`CANVAS_OBJECT_TAGS_EXPORT` set to a new absolute directory while running the test, then
+cold-open it with `tools/native_runner.py OUTPUT COLD --expected-pages 4 --collect-notebook --screenshots`.
+
+`restored/` is the output of `a_restored_picture_keeps_its_recognised_text`: the picture of
+text deleted and brought back by undo, written with its text (RichEditTextUnicode), its
+language and the undocumented layout of its words (0x1C001D61) as read, and its cold read,
+whose OCRData equals the native read's, words and positions alike
+(`CANVAS_RECOGNIZED_EXPORT`).
diff --git a/corpus/object-tags/candidate/Open Notebook.onetoc2 b/corpus/object-tags/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..e1692fd9fce225b307caec2c9991379898d29991
Binary files /dev/null and b/corpus/object-tags/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/object-tags/candidate/files.one b/corpus/object-tags/candidate/files.one
new file mode 100644
index 0000000000000000000000000000000000000000..970c722b4c8f1b0651b4bdef40b8263ef655679f
Binary files /dev/null and b/corpus/object-tags/candidate/files.one differ
diff --git a/corpus/object-tags/cold/commands.jsonl b/corpus/object-tags/cold/commands.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..17b08bd5ce6958fe50c8d35be112e2ca8d9db73c
--- /dev/null
+++ b/corpus/object-tags/cold/commands.jsonl
@@ -0,0 +1,3 @@
+{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-A6B430A7 -ExpectedPages 4 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 4 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/object-tags/cold/machine.json b/corpus/object-tags/cold/machine.json
new file mode 100644
index 0000000000000000000000000000000000000000..a82aef96f84ca600d8cc912b36832313b420f86a
--- /dev/null
+++ b/corpus/object-tags/cold/machine.json
@@ -0,0 +1 @@
+{"name": "m6-a6b430a7", "hostname": "ONE-M6-A6B430A7"}
diff --git a/corpus/object-tags/cold/notebook/Open Notebook.onetoc2 b/corpus/object-tags/cold/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..94133037d877e7c1e994e2f74e623c4e7039e6b3
Binary files /dev/null and b/corpus/object-tags/cold/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/object-tags/cold/notebook/files.one b/corpus/object-tags/cold/notebook/files.one
new file mode 100644
index 0000000000000000000000000000000000000000..c5fcc0072d49af2ff91765a0f625639e6cf00f04
Binary files /dev/null and b/corpus/object-tags/cold/notebook/files.one differ
diff --git a/corpus/object-tags/cold/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment b/corpus/object-tags/cold/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..3e82e7de059f9ddb683cf651a2c12114e2beb1bd
--- /dev/null
+++ b/corpus/object-tags/cold/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment
@@ -0,0 +1 @@
+Floating file, stored at page level.
diff --git a/corpus/object-tags/cold/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment b/corpus/object-tags/cold/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..8265454ede447c16d88088298dde04213d50d0be
--- /dev/null
+++ b/corpus/object-tags/cold/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment
@@ -0,0 +1 @@
+notes for tagging
diff --git a/corpus/object-tags/cold/read/environment.json b/corpus/object-tags/cold/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..14b40864988951ca7fca4b3701042d0b14cc263d
--- /dev/null
+++ b/corpus/object-tags/cold/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-A6B430A7",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/object-tags/cold/read/hierarchy.xml b/corpus/object-tags/cold/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e97d3b0a528e3e01541f8c26cd6268ec5f24a38b
--- /dev/null
+++ b/corpus/object-tags/cold/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/cold/read/page-000.navigation.json b/corpus/object-tags/cold/read/page-000.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..fdd4663bcd809095dbccb3a5bd8337dd95a9e8d0
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-000.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Pictures and files - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/cold/read/page-000.png b/corpus/object-tags/cold/read/page-000.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c372a14829d9eeaec22d90e0f68cc6fbfc6ff63
Binary files /dev/null and b/corpus/object-tags/cold/read/page-000.png differ
diff --git a/corpus/object-tags/cold/read/page-000.xml b/corpus/object-tags/cold/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c7dca86145203ef723921f0a9cb072af40435661
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-000.xml
@@ -0,0 +1,194 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAIAAAD6wG44AAACWklEQVR4nO3dy1EkQRAE0WYMsRCh
+DoiAGIiDCBwQYsVaFcCgKyK93M8cJvNZ9vAzeHp5/3cZt0f6Bdi9CQxPYHgCwxMYnsDwBIYnMDyB
+4QkMT2B4AsMTGJ7A8ASGJzA8geEJDE9geM8Xt6/Xt+9/8Pr8uIg9YX7p7kec55DPBv5zVB72SOBt
+rgDpScBB17nSM4CraGcxtwPX0k5h7gUeQdvPXPqNjnG6V+trrrvgzjXNPeWuCwboXmVTtFxw1VJI
+p1xxwUjdq2OuPHDDFsDTPQ6fHz9jEvgE3fikMeBzdLPzZoBP0w1OHQA+Uzc1+27gk3UjG8h/mWQc
+YM93/x72Aasb2YaPaHibgD3f1E68YHg7gD3f4Ga8YHi3A3u+2f14wfAEhncvsM/n+Ja8YHgCwxMY
+3o3AvgE37MoLhicwPIHhCQxPYHgCwxMYnsDwBIYnMLwH+w8YTOm+XXnB8ASGJzC8e4F9G45vyQuG
+JzC824F9Smf34wXD2wHsEQc34wXD2wTsEad24gXD2wfsEUe2sfWCNd6/Bx/R8HYDe8Rr74/JAxd8
+svHaPnvmEX2m8UpMHXsPPs14heZNfpJ1jvHKTRr+LPoE4xWdMf9lEtt4pafLAzdsATxXy7+2g/3V
+h1VAW3TBhXvBTNF1wdNPeTXRNl5w86aGvubSC551yquSdgZwOfMqpp0EXMi86mnnATdIryGus4H3
+S69prhDgW7HXWFQm8C/JF4LzLGDr/UaH/VUCwxMYnsDwBIYnMDyB4QkMT2B4AsMTGJ7A8ASGJzA8
+geEJDE9geAJf7P4Dry+9j2KmlKMAAAAASUVORK5CYII=iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAIAAAD6wG44AAACWklEQVR4nO3dy1EkQRAE0WYMsRCh
+DoiAGIiDCBwQYsVaFcCgKyK93M8cJvNZ9vAzeHp5/3cZt0f6Bdi9CQxPYHgCwxMYnsDwBIYnMDyB
+4QkMT2B4AsMTGJ7A8ASGJzA8geEJDE9geM8Xt6/Xt+9/8Pr8uIg9YX7p7kec55DPBv5zVB72SOBt
+rgDpScBB17nSM4CraGcxtwPX0k5h7gUeQdvPXPqNjnG6V+trrrvgzjXNPeWuCwboXmVTtFxw1VJI
+p1xxwUjdq2OuPHDDFsDTPQ6fHz9jEvgE3fikMeBzdLPzZoBP0w1OHQA+Uzc1+27gk3UjG8h/mWQc
+YM93/x72Aasb2YaPaHibgD3f1E68YHg7gD3f4Ga8YHi3A3u+2f14wfAEhncvsM/n+Ja8YHgCwxMY
+3o3AvgE37MoLhicwPIHhCQxPYHgCwxMYnsDwBIYnMLwH+w8YTOm+XXnB8ASGJzC8e4F9G45vyQuG
+JzC824F9Smf34wXD2wHsEQc34wXD2wTsEad24gXD2wfsEUe2sfWCNd6/Bx/R8HYDe8Rr74/JAxd8
+svHaPnvmEX2m8UpMHXsPPs14heZNfpJ1jvHKTRr+LPoE4xWdMf9lEtt4pafLAzdsATxXy7+2g/3V
+h1VAW3TBhXvBTNF1wdNPeTXRNl5w86aGvubSC551yquSdgZwOfMqpp0EXMi86mnnATdIryGus4H3
+S69prhDgW7HXWFQm8C/JF4LzLGDr/UaH/VUCwxMYnsDwBIYnMDyB4QkMT2B4AsMTGJ7A8ASGJzA8
+geEJDE9geAJf7P4Dry+9j2KmlKMAAAAASUVORK5CYII=https://example.invalid/picture]]>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
++38UfRlkE+m5vQAAAABJRU5ErkJggg==
diff --git a/corpus/object-tags/cold/read/page-001.navigation.json b/corpus/object-tags/cold/read/page-001.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-001.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/cold/read/page-001.png b/corpus/object-tags/cold/read/page-001.png
new file mode 100644
index 0000000000000000000000000000000000000000..9a2e38b5b0096c3324a63f647b36dfcd71051025
Binary files /dev/null and b/corpus/object-tags/cold/read/page-001.png differ
diff --git a/corpus/object-tags/cold/read/page-001.xml b/corpus/object-tags/cold/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e25e8789bd598fef360b19f213bf7853e060b43b
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-001.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/cold/read/page-002.navigation.json b/corpus/object-tags/cold/read/page-002.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe822f91dcc10651bf314b0515da171fe0c1b116
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-002.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "https://example.invalid/picture - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/cold/read/page-002.png b/corpus/object-tags/cold/read/page-002.png
new file mode 100644
index 0000000000000000000000000000000000000000..a648c751c828a297fe76c1376bc8661970bb4a74
Binary files /dev/null and b/corpus/object-tags/cold/read/page-002.png differ
diff --git a/corpus/object-tags/cold/read/page-002.xml b/corpus/object-tags/cold/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..df2bbd9821dbb6b6bb4f2cae664af43f0a1c77ad
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-002.xml
@@ -0,0 +1,3 @@
+
+https://example.invalid/picture]]>
diff --git a/corpus/object-tags/cold/read/page-003.navigation.json b/corpus/object-tags/cold/read/page-003.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-003.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/cold/read/page-003.png b/corpus/object-tags/cold/read/page-003.png
new file mode 100644
index 0000000000000000000000000000000000000000..cffd0534bb9b5b385b640aea97a85e5d16346076
Binary files /dev/null and b/corpus/object-tags/cold/read/page-003.png differ
diff --git a/corpus/object-tags/cold/read/page-003.xml b/corpus/object-tags/cold/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bb1a6d0af5c8d5244780673f5b1ba1505678fbc9
--- /dev/null
+++ b/corpus/object-tags/cold/read/page-003.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/cold/read/payloads.json b/corpus/object-tags/cold/read/payloads.json
new file mode 100644
index 0000000000000000000000000000000000000000..fdcb9f97d728cd3c99dd1bc204e1db870bdaadda
--- /dev/null
+++ b/corpus/object-tags/cold/read/payloads.json
@@ -0,0 +1,34 @@
+[
+ {
+ "sha256": "33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6",
+ "name": "notes.txt",
+ "object": "{FCE9E77A-D017-4FFB-945E-0F0A0257D459}{48}{B0}",
+ "kind": "InsertedFile",
+ "page": "{5BFA1C0D-BE94-4F85-9BBD-51672E337B30}{1}{B0}",
+ "bytes": 18
+ },
+ {
+ "sha256": "33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6",
+ "name": "notes.txt",
+ "object": "{FCE9E77A-D017-4FFB-945E-0F0A0257D459}{59}{B0}",
+ "kind": "InsertedFile",
+ "page": "{5BFA1C0D-BE94-4F85-9BBD-51672E337B30}{1}{B0}",
+ "bytes": 18
+ },
+ {
+ "sha256": "32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89",
+ "name": "float.txt",
+ "object": "{C2A94E46-18C9-06D4-1EC8-8D5328B30769}{40}{B0}",
+ "kind": "InsertedFile",
+ "page": "{62908E22-534B-06F1-04E8-8159D324438A}{1}{B0}",
+ "bytes": 38
+ },
+ {
+ "sha256": "32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89",
+ "name": "float.txt",
+ "object": "{08585F32-BA6A-042A-288B-8FC98DC9C068}{18}{B0}",
+ "kind": "InsertedFile",
+ "page": "{62908E22-534B-06F1-04E8-8159D324438A}{1}{B0}",
+ "bytes": 38
+ }
+]
\ No newline at end of file
diff --git a/corpus/object-tags/cold/run.json b/corpus/object-tags/cold/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..0c9ac735dc6b3929983d0b21a8754fa4012bca0d
--- /dev/null
+++ b/corpus/object-tags/cold/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/private/tmp/pictab/candidate",
+ "expected_pages": 4,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35"
+ }
+}
diff --git a/corpus/object-tags/cold/scripts/cold.ps1 b/corpus/object-tags/cold/scripts/cold.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f
--- /dev/null
+++ b/corpus/object-tags/cold/scripts/cold.ps1
@@ -0,0 +1,27 @@
+param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '')
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' }
+$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote'
+if ($CloneHost) {
+ if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) {
+ throw 'The disposable clone hostname does not match this machine.'
+ }
+ New-Item "$key\Options\Paths" -Force | Out-Null
+ New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null
+} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or
+ -not (Test-Path 'C:\one-tests\profile-original-cache')) {
+ throw 'Park the personal OneNote profile before resetting the test cache.'
+}
+$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0'
+$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root))
+if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' }
+New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null
+if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked }
+if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse }
+New-Item "$key\OpenNotebooks" | Out-Null
+New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null
diff --git a/corpus/object-tags/cold/scripts/read.ps1 b/corpus/object-tags/cold/scripts/read.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc
--- /dev/null
+++ b/corpus/object-tags/cold/scripts/read.ps1
@@ -0,0 +1,144 @@
+param(
+ [Parameter(Mandatory=$true)][string]$Root,
+ [int]$ExpectedPages = -1,
+ [switch]$UseCurrentCache,
+ [switch]$Pdf,
+ [switch]$KeepOpen,
+ [string]$CloneHost = ''
+)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+$notebook = Join-Path $root 'notebook'
+$output = Join-Path $root 'read'
+if (Test-Path $output) { throw 'Choose a new read destination.' }
+if ($UseCurrentCache) {
+ if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') {
+ throw 'Park the personal OneNote profile before reading test notebooks.'
+ }
+} else {
+ & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost
+}
+New-Item -ItemType Directory -Path $output | Out-Null
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+$failure = $null
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $process = Get-Process ONENOTE
+ @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion;
+ powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } |
+ ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8
+ $sections = @()
+ foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) {
+ $id = ''
+ $app.OpenHierarchy($file.FullName, '', [ref]$id, 0)
+ $sections += $id
+ }
+ $deadline = [DateTime]::UtcNow.AddSeconds(300)
+ $previous = ''
+ $lastChange = ''
+ $stableSince = [DateTime]::UtcNow
+ $settled = $false
+ do {
+ $pages = @{}
+ foreach ($section in $sections) {
+ $hierarchy = ''
+ $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
+ [xml]$xml = $hierarchy
+ foreach ($node in $xml.SelectNodes('//*[@path]')) {
+ if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'OneNote opened a section outside the copied notebook.'
+ }
+ }
+ foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) {
+ $id = $node.GetAttribute('ID')
+ $content = ''
+ $app.GetPageContent($id, [ref]$content, 1, 1)
+ $pages[$id] = $content
+ }
+ }
+ $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] }))
+ if ($signature -ne $previous) {
+ $lastChange = $previous
+ $previous = $signature
+ $stableSince = [DateTime]::UtcNow
+ }
+ if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or
+ ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and
+ ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break }
+ Start-Sleep -Milliseconds 250
+ } while ([DateTime]::UtcNow -lt $deadline)
+ if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) {
+ [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8)
+ $index = 0
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ $index++
+ }
+ $hierarchy = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1)
+ [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8)
+ throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled."
+ }
+ $index = 0
+ $payloads = @()
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ if ($Pdf) {
+ $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index)
+ $app.NavigateTo($id, '', $false)
+ $app.Publish($id, $pdfPath, 3, '')
+ if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' }
+ }
+ [xml]$page = $pages[$id]
+ foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) {
+ $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache'))
+ $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant()
+ [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes)
+ # A paragraph's file is named by its paragraph, one on the page by itself.
+ $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode }
+ $payloads += @{ page = $id; object = $owner.GetAttribute('objectID');
+ kind = $file.LocalName; name = $file.GetAttribute('preferredName');
+ sha256 = $hash; bytes = $bytes.Length }
+ }
+ $index++
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8)
+ $all = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$all, 1)
+ [xml]$finalTree = $all
+ $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique)
+ if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) {
+ throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.'
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8)
+ Write-Output "Read $($sections.Count) sections and $($pages.Count) pages."
+} catch {
+ $failure = $_
+ [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ throw
+} finally {
+ try {
+ try {
+ if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) }
+ if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) {
+ $app.CloseNotebook($notebookId, $false)
+ }
+ } catch {
+ if ($null -eq $failure) { throw }
+ [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ }
+ } finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ }
+ $app = $null
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+ if (-not $UseCurrentCache -and -not $CloneHost) {
+ Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10
+ }
+}
diff --git a/corpus/object-tags/cold/source.json b/corpus/object-tags/cold/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..404a8db443a8cbb9ad71cfdce0cf2b26e3f400bb
--- /dev/null
+++ b/corpus/object-tags/cold/source.json
@@ -0,0 +1,14 @@
+[
+ {
+ "path": "Open Notebook.onetoc2",
+ "bytes": 1604,
+ "sha256": "f3a3df7f649fd730cc071103b4ab50fa7bdf3c4df0c4296ae0c3fb640ebee1ad",
+ "mtime_ns": 1790808646958031593
+ },
+ {
+ "path": "files.one",
+ "bytes": 59048,
+ "sha256": "7222f2c26057540dd2a537b337e5667d8140e4fdb2de41d091b09b39fdce5edc",
+ "mtime_ns": 1790808646957690287
+ }
+]
diff --git a/corpus/object-tags/cold/teardown.json b/corpus/object-tags/cold/teardown.json
new file mode 100644
index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70
--- /dev/null
+++ b/corpus/object-tags/cold/teardown.json
@@ -0,0 +1 @@
+{"absent": true}
diff --git a/corpus/object-tags/native/commands.jsonl b/corpus/object-tags/native/commands.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..3296bfd224c0ed79ddc61ef379163b0db4c9aff5
--- /dev/null
+++ b/corpus/object-tags/native/commands.jsonl
@@ -0,0 +1,7 @@
+{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\author.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-F27645BE", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-F27645BE -ExpectedPages -1 -UseCurrentCache -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 3 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Rename-Item C:\\one-tests\\runs\\capture\\read before-read\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-F27645BE -UseCurrentCache -ExpectedPages -1", "exit": 0, "stdout": "Read 1 sections and 4 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/object-tags/native/machine.json b/corpus/object-tags/native/machine.json
new file mode 100644
index 0000000000000000000000000000000000000000..9bb6fa485c5499156b072b623ad599e0a6734dc7
--- /dev/null
+++ b/corpus/object-tags/native/machine.json
@@ -0,0 +1 @@
+{"name": "m6-f27645be", "hostname": "ONE-M6-F27645BE"}
diff --git a/corpus/object-tags/native/notebook/Open Notebook.onetoc2 b/corpus/object-tags/native/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..fcb77261b617e8f3642da9bb5fa16150a744da01
Binary files /dev/null and b/corpus/object-tags/native/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/object-tags/native/notebook/files.one b/corpus/object-tags/native/notebook/files.one
new file mode 100644
index 0000000000000000000000000000000000000000..9b9289ace3c2fea44975f23043362d897e469e19
Binary files /dev/null and b/corpus/object-tags/native/notebook/files.one differ
diff --git a/corpus/object-tags/native/notebook/notes.txt b/corpus/object-tags/native/notebook/notes.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8265454ede447c16d88088298dde04213d50d0be
--- /dev/null
+++ b/corpus/object-tags/native/notebook/notes.txt
@@ -0,0 +1 @@
+notes for tagging
diff --git a/corpus/object-tags/native/notebook/ocr.png b/corpus/object-tags/native/notebook/ocr.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb9c1b4bef6aca8377810f2a23fe330f3d9713fe
Binary files /dev/null and b/corpus/object-tags/native/notebook/ocr.png differ
diff --git a/corpus/object-tags/native/notebook/photo.png b/corpus/object-tags/native/notebook/photo.png
new file mode 100644
index 0000000000000000000000000000000000000000..345db618cf2a756521ab7f100deb49b639e00b0b
Binary files /dev/null and b/corpus/object-tags/native/notebook/photo.png differ
diff --git a/corpus/object-tags/native/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment b/corpus/object-tags/native/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..3e82e7de059f9ddb683cf651a2c12114e2beb1bd
--- /dev/null
+++ b/corpus/object-tags/native/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment
@@ -0,0 +1 @@
+Floating file, stored at page level.
diff --git a/corpus/object-tags/native/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment b/corpus/object-tags/native/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..8265454ede447c16d88088298dde04213d50d0be
--- /dev/null
+++ b/corpus/object-tags/native/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment
@@ -0,0 +1 @@
+notes for tagging
diff --git a/corpus/object-tags/native/read/environment.json b/corpus/object-tags/native/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3468069a8dcb39647aae991dcd24404065441bb
--- /dev/null
+++ b/corpus/object-tags/native/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-F27645BE",
+ "cold": false,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/object-tags/native/read/hierarchy.xml b/corpus/object-tags/native/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8c7ce81b0013a365654efb96ec6c881585004796
--- /dev/null
+++ b/corpus/object-tags/native/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/native/read/page-000.xml b/corpus/object-tags/native/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..702e7d4df59db9ea29d25c15f0eae292fe0cddaa
--- /dev/null
+++ b/corpus/object-tags/native/read/page-000.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/native/read/page-001.xml b/corpus/object-tags/native/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..16bd93e03e94d49ba5ccf91f3dcfce71e23e9bec
--- /dev/null
+++ b/corpus/object-tags/native/read/page-001.xml
@@ -0,0 +1,194 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAIAAAD6wG44AAACWklEQVR4nO3dy1EkQRAE0WYMsRCh
+DoiAGIiDCBwQYsVaFcCgKyK93M8cJvNZ9vAzeHp5/3cZt0f6Bdi9CQxPYHgCwxMYnsDwBIYnMDyB
+4QkMT2B4AsMTGJ7A8ASGJzA8geEJDE9geM8Xt6/Xt+9/8Pr8uIg9YX7p7kec55DPBv5zVB72SOBt
+rgDpScBB17nSM4CraGcxtwPX0k5h7gUeQdvPXPqNjnG6V+trrrvgzjXNPeWuCwboXmVTtFxw1VJI
+p1xxwUjdq2OuPHDDFsDTPQ6fHz9jEvgE3fikMeBzdLPzZoBP0w1OHQA+Uzc1+27gk3UjG8h/mWQc
+YM93/x72Aasb2YaPaHibgD3f1E68YHg7gD3f4Ga8YHi3A3u+2f14wfAEhncvsM/n+Ja8YHgCwxMY
+3o3AvgE37MoLhicwPIHhCQxPYHgCwxMYnsDwBIYnMLwH+w8YTOm+XXnB8ASGJzC8e4F9G45vyQuG
+JzC824F9Smf34wXD2wHsEQc34wXD2wTsEad24gXD2wfsEUe2sfWCNd6/Bx/R8HYDe8Rr74/JAxd8
+svHaPnvmEX2m8UpMHXsPPs14heZNfpJ1jvHKTRr+LPoE4xWdMf9lEtt4pafLAzdsATxXy7+2g/3V
+h1VAW3TBhXvBTNF1wdNPeTXRNl5w86aGvubSC551yquSdgZwOfMqpp0EXMi86mnnATdIryGus4H3
+S69prhDgW7HXWFQm8C/JF4LzLGDr/UaH/VUCwxMYnsDwBIYnMDyB4QkMT2B4AsMTGJ7A8ASGJzA8
+geEJDE9geAJf7P4Dry+9j2KmlKMAAAAASUVORK5CYII=iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAIAAAD6wG44AAACWklEQVR4nO3dy1EkQRAE0WYMsRCh
+DoiAGIiDCBwQYsVaFcCgKyK93M8cJvNZ9vAzeHp5/3cZt0f6Bdi9CQxPYHgCwxMYnsDwBIYnMDyB
+4QkMT2B4AsMTGJ7A8ASGJzA8geEJDE9geM8Xt6/Xt+9/8Pr8uIg9YX7p7kec55DPBv5zVB72SOBt
+rgDpScBB17nSM4CraGcxtwPX0k5h7gUeQdvPXPqNjnG6V+trrrvgzjXNPeWuCwboXmVTtFxw1VJI
+p1xxwUjdq2OuPHDDFsDTPQ6fHz9jEvgE3fikMeBzdLPzZoBP0w1OHQA+Uzc1+27gk3UjG8h/mWQc
+YM93/x72Aasb2YaPaHibgD3f1E68YHg7gD3f4Ga8YHi3A3u+2f14wfAEhncvsM/n+Ja8YHgCwxMY
+3o3AvgE37MoLhicwPIHhCQxPYHgCwxMYnsDwBIYnMLwH+w8YTOm+XXnB8ASGJzC8e4F9G45vyQuG
+JzC824F9Smf34wXD2wHsEQc34wXD2wTsEad24gXD2wfsEUe2sfWCNd6/Bx/R8HYDe8Rr74/JAxd8
+svHaPnvmEX2m8UpMHXsPPs14heZNfpJ1jvHKTRr+LPoE4xWdMf9lEtt4pafLAzdsATxXy7+2g/3V
+h1VAW3TBhXvBTNF1wdNPeTXRNl5w86aGvubSC551yquSdgZwOfMqpp0EXMi86mnnATdIryGus4H3
+S69prhDgW7HXWFQm8C/JF4LzLGDr/UaH/VUCwxMYnsDwBIYnMDyB4QkMT2B4AsMTGJ7A8ASGJzA8
+geEJDE9geAJf7P4Dry+9j2KmlKMAAAAASUVORK5CYII=https://example.invalid/picture]]>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
++38UfRlkE+m5vQAAAABJRU5ErkJggg==
diff --git a/corpus/object-tags/native/read/page-002.xml b/corpus/object-tags/native/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9630e2cb89d9de7450571d31b03a35530fa2eecf
--- /dev/null
+++ b/corpus/object-tags/native/read/page-002.xml
@@ -0,0 +1,3 @@
+
+https://example.invalid/picture]]>
diff --git a/corpus/object-tags/native/read/page-003.xml b/corpus/object-tags/native/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..afc7400afe3d48ac01bd928d2c12462d65e409b5
--- /dev/null
+++ b/corpus/object-tags/native/read/page-003.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/native/read/payloads.json b/corpus/object-tags/native/read/payloads.json
new file mode 100644
index 0000000000000000000000000000000000000000..ed14332dfef492b872ed6767c7ba8e394aa834f8
--- /dev/null
+++ b/corpus/object-tags/native/read/payloads.json
@@ -0,0 +1,34 @@
+[
+ {
+ "sha256": "32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89",
+ "name": "float.txt",
+ "object": "{EC8778C0-4C56-0389-38D4-AE4F82C56A97}{40}{B0}",
+ "kind": "InsertedFile",
+ "page": "{4CBEB8A4-07D4-03AC-22F4-A24579522E74}{1}{B0}",
+ "bytes": 38
+ },
+ {
+ "sha256": "32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89",
+ "name": "float.txt",
+ "object": "{267669B4-EEF5-0177-0E97-ACD527BFAD96}{18}{B0}",
+ "kind": "InsertedFile",
+ "page": "{4CBEB8A4-07D4-03AC-22F4-A24579522E74}{1}{B0}",
+ "bytes": 38
+ },
+ {
+ "sha256": "33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6",
+ "name": "notes.txt",
+ "object": "{D2C7D1FC-8488-4AA6-B242-2C16A821B9A7}{48}{B0}",
+ "kind": "InsertedFile",
+ "page": "{75D42A8B-EA0B-4AD8-BDA1-727B844516CE}{1}{B0}",
+ "bytes": 18
+ },
+ {
+ "sha256": "33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6",
+ "name": "notes.txt",
+ "object": "{D2C7D1FC-8488-4AA6-B242-2C16A821B9A7}{59}{B0}",
+ "kind": "InsertedFile",
+ "page": "{75D42A8B-EA0B-4AD8-BDA1-727B844516CE}{1}{B0}",
+ "bytes": 18
+ }
+]
\ No newline at end of file
diff --git a/corpus/object-tags/native/run.json b/corpus/object-tags/native/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..afd06592ffe1dc43e16595a70c660c8581b482fa
--- /dev/null
+++ b/corpus/object-tags/native/run.json
@@ -0,0 +1,19 @@
+{
+ "notebook": "/private/tmp/pictab/nb",
+ "expected_pages": -1,
+ "author": "tools/native/picture-objects.ps1",
+ "author_timeout_seconds": 600,
+ "inspect": true,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "author.ps1": "9402c73adc2a48971075f1ead915ee095b5d4abf8d09cdc9fb9473697aebe4c0",
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35"
+ }
+}
diff --git a/corpus/object-tags/native/scripts/author.ps1 b/corpus/object-tags/native/scripts/author.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..c5c6bd498e29ad9acb82a7731320c5c1542a1688
--- /dev/null
+++ b/corpus/object-tags/native/scripts/author.ps1
@@ -0,0 +1,37 @@
+param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost)
+# A page of pictures and files, in an outline and on the page, for tagging, linking and OCR
+# by hand in the clone: notebook/photo.png, notebook/ocr.png and notebook/notes.txt ride along
+# with the section.
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost
+$namespace = 'http://schemas.microsoft.com/office/onenote/2010/onenote'
+$notebook = Join-Path $Root 'notebook'
+$data = { param($name) [Convert]::ToBase64String([IO.File]::ReadAllBytes((Join-Path $notebook $name))) }
+$file = Join-Path $notebook 'notes.txt'
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $sectionId = ''
+ $app.OpenHierarchy('files.one', $notebookId, [ref]$sectionId, 0)
+ $pageId = ''
+ $app.CreateNewPage($sectionId, [ref]$pageId, 0)
+ $body = ''
+ $body += ''
+ $body += ''
+ $body += '' + (& $data 'photo.png') + ''
+ $body += ''
+ $body += ''
+ $body += ''
+ $body += '' + (& $data 'photo.png') + ''
+ $body += ''
+ $body += '' + (& $data 'ocr.png') + ''
+ $xml = '' + $body + ''
+ $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $true)
+ $app.SyncHierarchy($notebookId)
+} finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+}
diff --git a/corpus/object-tags/native/scripts/cold.ps1 b/corpus/object-tags/native/scripts/cold.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f
--- /dev/null
+++ b/corpus/object-tags/native/scripts/cold.ps1
@@ -0,0 +1,27 @@
+param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '')
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' }
+$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote'
+if ($CloneHost) {
+ if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) {
+ throw 'The disposable clone hostname does not match this machine.'
+ }
+ New-Item "$key\Options\Paths" -Force | Out-Null
+ New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null
+} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or
+ -not (Test-Path 'C:\one-tests\profile-original-cache')) {
+ throw 'Park the personal OneNote profile before resetting the test cache.'
+}
+$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0'
+$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root))
+if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' }
+New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null
+if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked }
+if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse }
+New-Item "$key\OpenNotebooks" | Out-Null
+New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null
diff --git a/corpus/object-tags/native/scripts/read.ps1 b/corpus/object-tags/native/scripts/read.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc
--- /dev/null
+++ b/corpus/object-tags/native/scripts/read.ps1
@@ -0,0 +1,144 @@
+param(
+ [Parameter(Mandatory=$true)][string]$Root,
+ [int]$ExpectedPages = -1,
+ [switch]$UseCurrentCache,
+ [switch]$Pdf,
+ [switch]$KeepOpen,
+ [string]$CloneHost = ''
+)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+$notebook = Join-Path $root 'notebook'
+$output = Join-Path $root 'read'
+if (Test-Path $output) { throw 'Choose a new read destination.' }
+if ($UseCurrentCache) {
+ if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') {
+ throw 'Park the personal OneNote profile before reading test notebooks.'
+ }
+} else {
+ & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost
+}
+New-Item -ItemType Directory -Path $output | Out-Null
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+$failure = $null
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $process = Get-Process ONENOTE
+ @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion;
+ powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } |
+ ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8
+ $sections = @()
+ foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) {
+ $id = ''
+ $app.OpenHierarchy($file.FullName, '', [ref]$id, 0)
+ $sections += $id
+ }
+ $deadline = [DateTime]::UtcNow.AddSeconds(300)
+ $previous = ''
+ $lastChange = ''
+ $stableSince = [DateTime]::UtcNow
+ $settled = $false
+ do {
+ $pages = @{}
+ foreach ($section in $sections) {
+ $hierarchy = ''
+ $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
+ [xml]$xml = $hierarchy
+ foreach ($node in $xml.SelectNodes('//*[@path]')) {
+ if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'OneNote opened a section outside the copied notebook.'
+ }
+ }
+ foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) {
+ $id = $node.GetAttribute('ID')
+ $content = ''
+ $app.GetPageContent($id, [ref]$content, 1, 1)
+ $pages[$id] = $content
+ }
+ }
+ $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] }))
+ if ($signature -ne $previous) {
+ $lastChange = $previous
+ $previous = $signature
+ $stableSince = [DateTime]::UtcNow
+ }
+ if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or
+ ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and
+ ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break }
+ Start-Sleep -Milliseconds 250
+ } while ([DateTime]::UtcNow -lt $deadline)
+ if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) {
+ [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8)
+ $index = 0
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ $index++
+ }
+ $hierarchy = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1)
+ [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8)
+ throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled."
+ }
+ $index = 0
+ $payloads = @()
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ if ($Pdf) {
+ $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index)
+ $app.NavigateTo($id, '', $false)
+ $app.Publish($id, $pdfPath, 3, '')
+ if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' }
+ }
+ [xml]$page = $pages[$id]
+ foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) {
+ $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache'))
+ $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant()
+ [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes)
+ # A paragraph's file is named by its paragraph, one on the page by itself.
+ $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode }
+ $payloads += @{ page = $id; object = $owner.GetAttribute('objectID');
+ kind = $file.LocalName; name = $file.GetAttribute('preferredName');
+ sha256 = $hash; bytes = $bytes.Length }
+ }
+ $index++
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8)
+ $all = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$all, 1)
+ [xml]$finalTree = $all
+ $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique)
+ if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) {
+ throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.'
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8)
+ Write-Output "Read $($sections.Count) sections and $($pages.Count) pages."
+} catch {
+ $failure = $_
+ [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ throw
+} finally {
+ try {
+ try {
+ if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) }
+ if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) {
+ $app.CloseNotebook($notebookId, $false)
+ }
+ } catch {
+ if ($null -eq $failure) { throw }
+ [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ }
+ } finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ }
+ $app = $null
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+ if (-not $UseCurrentCache -and -not $CloneHost) {
+ Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10
+ }
+}
diff --git a/corpus/object-tags/native/source.json b/corpus/object-tags/native/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..2a12196a2cfb9589d4133dcf9d2038561d53a0b0
--- /dev/null
+++ b/corpus/object-tags/native/source.json
@@ -0,0 +1,26 @@
+[
+ {
+ "path": "files.one",
+ "bytes": 21160,
+ "sha256": "d7d7688e86bae61505b96daf8af0d5d7adc1af3777ac13f2551cc8a339c1a182",
+ "mtime_ns": 1790805042299289270
+ },
+ {
+ "path": "notes.txt",
+ "bytes": 18,
+ "sha256": "33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6",
+ "mtime_ns": 1790805042459716143
+ },
+ {
+ "path": "ocr.png",
+ "bytes": 9598,
+ "sha256": "b81dc7dd998956abfc1394b45567140c178b7b4523d06726970f75f4a2b701db",
+ "mtime_ns": 1790805042459075351
+ },
+ {
+ "path": "photo.png",
+ "bytes": 659,
+ "sha256": "2a39fa803c359c78ce662012ae75b810a33b13647c0050c29176301dcad8eb3b",
+ "mtime_ns": 1790805042459606226
+ }
+]
diff --git a/corpus/object-tags/native/teardown.json b/corpus/object-tags/native/teardown.json
new file mode 100644
index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70
--- /dev/null
+++ b/corpus/object-tags/native/teardown.json
@@ -0,0 +1 @@
+{"absent": true}
diff --git a/corpus/object-tags/restored/candidate/Open Notebook.onetoc2 b/corpus/object-tags/restored/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..920f40e2c6b85f71dc2755f9187c5211c7d64707
Binary files /dev/null and b/corpus/object-tags/restored/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/object-tags/restored/candidate/files.one b/corpus/object-tags/restored/candidate/files.one
new file mode 100644
index 0000000000000000000000000000000000000000..6fb0d8aeaf062502200803a3d4de259ce5984897
Binary files /dev/null and b/corpus/object-tags/restored/candidate/files.one differ
diff --git a/corpus/object-tags/restored/cold/commands.jsonl b/corpus/object-tags/restored/cold/commands.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..cc850632a679c76627e4ca4bb69ba8bfe6a804d7
--- /dev/null
+++ b/corpus/object-tags/restored/cold/commands.jsonl
@@ -0,0 +1,3 @@
+{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-98295DF8 -ExpectedPages 4 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 4 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/object-tags/restored/cold/machine.json b/corpus/object-tags/restored/cold/machine.json
new file mode 100644
index 0000000000000000000000000000000000000000..71a7e90865672f722aaabae0e81a987e90b65fb3
--- /dev/null
+++ b/corpus/object-tags/restored/cold/machine.json
@@ -0,0 +1 @@
+{"name": "m6-98295df8", "hostname": "ONE-M6-98295DF8"}
diff --git a/corpus/object-tags/restored/cold/notebook/Open Notebook.onetoc2 b/corpus/object-tags/restored/cold/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..7b2ca00ae65c4aed9a2e42d3792970cfd030b470
Binary files /dev/null and b/corpus/object-tags/restored/cold/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/object-tags/restored/cold/notebook/files.one b/corpus/object-tags/restored/cold/notebook/files.one
new file mode 100644
index 0000000000000000000000000000000000000000..a85e6208e30a967ee8141f5dc177c31992cff4a9
Binary files /dev/null and b/corpus/object-tags/restored/cold/notebook/files.one differ
diff --git a/corpus/object-tags/restored/cold/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment b/corpus/object-tags/restored/cold/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..3e82e7de059f9ddb683cf651a2c12114e2beb1bd
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89.attachment
@@ -0,0 +1 @@
+Floating file, stored at page level.
diff --git a/corpus/object-tags/restored/cold/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment b/corpus/object-tags/restored/cold/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..8265454ede447c16d88088298dde04213d50d0be
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6.attachment
@@ -0,0 +1 @@
+notes for tagging
diff --git a/corpus/object-tags/restored/cold/read/environment.json b/corpus/object-tags/restored/cold/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..7ef854cd864b4fc5ea87d42d119ab49c5de70209
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-98295DF8",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/object-tags/restored/cold/read/hierarchy.xml b/corpus/object-tags/restored/cold/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4a50855cd8afaef867f88d959d66f71d50772a4a
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/restored/cold/read/page-000.navigation.json b/corpus/object-tags/restored/cold/read/page-000.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe822f91dcc10651bf314b0515da171fe0c1b116
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-000.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "https://example.invalid/picture - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/restored/cold/read/page-000.png b/corpus/object-tags/restored/cold/read/page-000.png
new file mode 100644
index 0000000000000000000000000000000000000000..a1ed6186f92299758ca1ea16e056ac4471e9ee73
Binary files /dev/null and b/corpus/object-tags/restored/cold/read/page-000.png differ
diff --git a/corpus/object-tags/restored/cold/read/page-000.xml b/corpus/object-tags/restored/cold/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ab326361d83d5169813548263cbe778abd44d93b
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-000.xml
@@ -0,0 +1,3 @@
+
+https://example.invalid/picture]]>
diff --git a/corpus/object-tags/restored/cold/read/page-001.navigation.json b/corpus/object-tags/restored/cold/read/page-001.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-001.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/restored/cold/read/page-001.png b/corpus/object-tags/restored/cold/read/page-001.png
new file mode 100644
index 0000000000000000000000000000000000000000..976b671d708cff406b9364aeaea1cf2d31e8b043
Binary files /dev/null and b/corpus/object-tags/restored/cold/read/page-001.png differ
diff --git a/corpus/object-tags/restored/cold/read/page-001.xml b/corpus/object-tags/restored/cold/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c97e943ecdc62f40b630e1da4257f5bb52059144
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-001.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/restored/cold/read/page-002.navigation.json b/corpus/object-tags/restored/cold/read/page-002.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-002.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/restored/cold/read/page-002.png b/corpus/object-tags/restored/cold/read/page-002.png
new file mode 100644
index 0000000000000000000000000000000000000000..15f42d825fcf90bcf4296c18c7eb4b1ec5b09d99
Binary files /dev/null and b/corpus/object-tags/restored/cold/read/page-002.png differ
diff --git a/corpus/object-tags/restored/cold/read/page-002.xml b/corpus/object-tags/restored/cold/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ef78507f28ef04e451624b299f2fcff822585174
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-002.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/object-tags/restored/cold/read/page-003.navigation.json b/corpus/object-tags/restored/cold/read/page-003.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..fdd4663bcd809095dbccb3a5bd8337dd95a9e8d0
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-003.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Pictures and files - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/object-tags/restored/cold/read/page-003.png b/corpus/object-tags/restored/cold/read/page-003.png
new file mode 100644
index 0000000000000000000000000000000000000000..944b5525d9648af4e9108ea6f9baad1f5bbd6c2c
Binary files /dev/null and b/corpus/object-tags/restored/cold/read/page-003.png differ
diff --git a/corpus/object-tags/restored/cold/read/page-003.xml b/corpus/object-tags/restored/cold/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..95e253bb034a80c6194c810dbfc15cec4176d2c9
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/page-003.xml
@@ -0,0 +1,194 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAIAAAD6wG44AAACWklEQVR4nO3dy1EkQRAE0WYMsRCh
+DoiAGIiDCBwQYsVaFcCgKyK93M8cJvNZ9vAzeHp5/3cZt0f6Bdi9CQxPYHgCwxMYnsDwBIYnMDyB
+4QkMT2B4AsMTGJ7A8ASGJzA8geEJDE9geM8Xt6/Xt+9/8Pr8uIg9YX7p7kec55DPBv5zVB72SOBt
+rgDpScBB17nSM4CraGcxtwPX0k5h7gUeQdvPXPqNjnG6V+trrrvgzjXNPeWuCwboXmVTtFxw1VJI
+p1xxwUjdq2OuPHDDFsDTPQ6fHz9jEvgE3fikMeBzdLPzZoBP0w1OHQA+Uzc1+27gk3UjG8h/mWQc
+YM93/x72Aasb2YaPaHibgD3f1E68YHg7gD3f4Ga8YHi3A3u+2f14wfAEhncvsM/n+Ja8YHgCwxMY
+3o3AvgE37MoLhicwPIHhCQxPYHgCwxMYnsDwBIYnMLwH+w8YTOm+XXnB8ASGJzC8e4F9G45vyQuG
+JzC824F9Smf34wXD2wHsEQc34wXD2wTsEad24gXD2wfsEUe2sfWCNd6/Bx/R8HYDe8Rr74/JAxd8
+svHaPnvmEX2m8UpMHXsPPs14heZNfpJ1jvHKTRr+LPoE4xWdMf9lEtt4pafLAzdsATxXy7+2g/3V
+h1VAW3TBhXvBTNF1wdNPeTXRNl5w86aGvubSC551yquSdgZwOfMqpp0EXMi86mnnATdIryGus4H3
+S69prhDgW7HXWFQm8C/JF4LzLGDr/UaH/VUCwxMYnsDwBIYnMDyB4QkMT2B4AsMTGJ7A8ASGJzA8
+geEJDE9geAJf7P4Dry+9j2KmlKMAAAAASUVORK5CYII=iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAIAAAD6wG44AAACWklEQVR4nO3dy1EkQRAE0WYMsRCh
+DoiAGIiDCBwQYsVaFcCgKyK93M8cJvNZ9vAzeHp5/3cZt0f6Bdi9CQxPYHgCwxMYnsDwBIYnMDyB
+4QkMT2B4AsMTGJ7A8ASGJzA8geEJDE9geM8Xt6/Xt+9/8Pr8uIg9YX7p7kec55DPBv5zVB72SOBt
+rgDpScBB17nSM4CraGcxtwPX0k5h7gUeQdvPXPqNjnG6V+trrrvgzjXNPeWuCwboXmVTtFxw1VJI
+p1xxwUjdq2OuPHDDFsDTPQ6fHz9jEvgE3fikMeBzdLPzZoBP0w1OHQA+Uzc1+27gk3UjG8h/mWQc
+YM93/x72Aasb2YaPaHibgD3f1E68YHg7gD3f4Ga8YHi3A3u+2f14wfAEhncvsM/n+Ja8YHgCwxMY
+3o3AvgE37MoLhicwPIHhCQxPYHgCwxMYnsDwBIYnMLwH+w8YTOm+XXnB8ASGJzC8e4F9G45vyQuG
+JzC824F9Smf34wXD2wHsEQc34wXD2wTsEad24gXD2wfsEUe2sfWCNd6/Bx/R8HYDe8Rr74/JAxd8
+svHaPnvmEX2m8UpMHXsPPs14heZNfpJ1jvHKTRr+LPoE4xWdMf9lEtt4pafLAzdsATxXy7+2g/3V
+h1VAW3TBhXvBTNF1wdNPeTXRNl5w86aGvubSC551yquSdgZwOfMqpp0EXMi86mnnATdIryGus4H3
+S69prhDgW7HXWFQm8C/JF4LzLGDr/UaH/VUCwxMYnsDwBIYnMDyB4QkMT2B4AsMTGJ7A8ASGJzA8
+geEJDE9geAJf7P4Dry+9j2KmlKMAAAAASUVORK5CYII=https://example.invalid/picture]]>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
++38UfRlkE+m5vQAAAABJRU5ErkJggg==
diff --git a/corpus/object-tags/restored/cold/read/payloads.json b/corpus/object-tags/restored/cold/read/payloads.json
new file mode 100644
index 0000000000000000000000000000000000000000..59a5887e624b39a847729dd8d86d02fce18d3cb8
--- /dev/null
+++ b/corpus/object-tags/restored/cold/read/payloads.json
@@ -0,0 +1,34 @@
+[
+ {
+ "sha256": "32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89",
+ "name": "float.txt",
+ "object": "{6266A23A-19F7-0158-3CC8-FA92C958A150}{40}{B0}",
+ "kind": "InsertedFile",
+ "page": "{C25F625E-5275-017D-26E8-F69832CFE5B3}{1}{B0}",
+ "bytes": 38
+ },
+ {
+ "sha256": "32a01b6d0a94deebc53c688868b14b6fccc4e18b3b64ea052823e3856cf35b89",
+ "name": "float.txt",
+ "object": "{A897B34E-BB54-03A6-0A8B-F8086C226651}{18}{B0}",
+ "kind": "InsertedFile",
+ "page": "{C25F625E-5275-017D-26E8-F69832CFE5B3}{1}{B0}",
+ "bytes": 38
+ },
+ {
+ "sha256": "33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6",
+ "name": "notes.txt",
+ "object": "{5C260B06-D129-4877-B65E-78CBE3BC7260}{48}{B0}",
+ "kind": "InsertedFile",
+ "page": "{FB35F071-BFAA-4809-B9BD-26A6CFD8DD09}{1}{B0}",
+ "bytes": 18
+ },
+ {
+ "sha256": "33fdf6e43c05ed910d335eda3fa269ccbd1415336c17a4a5357d009ee495b3a6",
+ "name": "notes.txt",
+ "object": "{5C260B06-D129-4877-B65E-78CBE3BC7260}{59}{B0}",
+ "kind": "InsertedFile",
+ "page": "{FB35F071-BFAA-4809-B9BD-26A6CFD8DD09}{1}{B0}",
+ "bytes": 18
+ }
+]
\ No newline at end of file
diff --git a/corpus/object-tags/restored/cold/run.json b/corpus/object-tags/restored/cold/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..934954c2aa295f96ba5859820ca5abbdf8dbd8de
--- /dev/null
+++ b/corpus/object-tags/restored/cold/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/private/tmp/ocrlab/candidate",
+ "expected_pages": 4,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35"
+ }
+}
diff --git a/corpus/object-tags/restored/cold/scripts/cold.ps1 b/corpus/object-tags/restored/cold/scripts/cold.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f
--- /dev/null
+++ b/corpus/object-tags/restored/cold/scripts/cold.ps1
@@ -0,0 +1,27 @@
+param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '')
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' }
+$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote'
+if ($CloneHost) {
+ if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) {
+ throw 'The disposable clone hostname does not match this machine.'
+ }
+ New-Item "$key\Options\Paths" -Force | Out-Null
+ New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null
+} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or
+ -not (Test-Path 'C:\one-tests\profile-original-cache')) {
+ throw 'Park the personal OneNote profile before resetting the test cache.'
+}
+$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0'
+$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root))
+if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' }
+New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null
+if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked }
+if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse }
+New-Item "$key\OpenNotebooks" | Out-Null
+New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null
diff --git a/corpus/object-tags/restored/cold/scripts/read.ps1 b/corpus/object-tags/restored/cold/scripts/read.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc
--- /dev/null
+++ b/corpus/object-tags/restored/cold/scripts/read.ps1
@@ -0,0 +1,144 @@
+param(
+ [Parameter(Mandatory=$true)][string]$Root,
+ [int]$ExpectedPages = -1,
+ [switch]$UseCurrentCache,
+ [switch]$Pdf,
+ [switch]$KeepOpen,
+ [string]$CloneHost = ''
+)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+$notebook = Join-Path $root 'notebook'
+$output = Join-Path $root 'read'
+if (Test-Path $output) { throw 'Choose a new read destination.' }
+if ($UseCurrentCache) {
+ if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') {
+ throw 'Park the personal OneNote profile before reading test notebooks.'
+ }
+} else {
+ & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost
+}
+New-Item -ItemType Directory -Path $output | Out-Null
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+$failure = $null
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $process = Get-Process ONENOTE
+ @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion;
+ powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } |
+ ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8
+ $sections = @()
+ foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) {
+ $id = ''
+ $app.OpenHierarchy($file.FullName, '', [ref]$id, 0)
+ $sections += $id
+ }
+ $deadline = [DateTime]::UtcNow.AddSeconds(300)
+ $previous = ''
+ $lastChange = ''
+ $stableSince = [DateTime]::UtcNow
+ $settled = $false
+ do {
+ $pages = @{}
+ foreach ($section in $sections) {
+ $hierarchy = ''
+ $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
+ [xml]$xml = $hierarchy
+ foreach ($node in $xml.SelectNodes('//*[@path]')) {
+ if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'OneNote opened a section outside the copied notebook.'
+ }
+ }
+ foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) {
+ $id = $node.GetAttribute('ID')
+ $content = ''
+ $app.GetPageContent($id, [ref]$content, 1, 1)
+ $pages[$id] = $content
+ }
+ }
+ $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] }))
+ if ($signature -ne $previous) {
+ $lastChange = $previous
+ $previous = $signature
+ $stableSince = [DateTime]::UtcNow
+ }
+ if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or
+ ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and
+ ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break }
+ Start-Sleep -Milliseconds 250
+ } while ([DateTime]::UtcNow -lt $deadline)
+ if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) {
+ [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8)
+ $index = 0
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ $index++
+ }
+ $hierarchy = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1)
+ [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8)
+ throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled."
+ }
+ $index = 0
+ $payloads = @()
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ if ($Pdf) {
+ $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index)
+ $app.NavigateTo($id, '', $false)
+ $app.Publish($id, $pdfPath, 3, '')
+ if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' }
+ }
+ [xml]$page = $pages[$id]
+ foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) {
+ $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache'))
+ $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant()
+ [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes)
+ # A paragraph's file is named by its paragraph, one on the page by itself.
+ $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode }
+ $payloads += @{ page = $id; object = $owner.GetAttribute('objectID');
+ kind = $file.LocalName; name = $file.GetAttribute('preferredName');
+ sha256 = $hash; bytes = $bytes.Length }
+ }
+ $index++
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8)
+ $all = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$all, 1)
+ [xml]$finalTree = $all
+ $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique)
+ if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) {
+ throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.'
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8)
+ Write-Output "Read $($sections.Count) sections and $($pages.Count) pages."
+} catch {
+ $failure = $_
+ [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ throw
+} finally {
+ try {
+ try {
+ if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) }
+ if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) {
+ $app.CloseNotebook($notebookId, $false)
+ }
+ } catch {
+ if ($null -eq $failure) { throw }
+ [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ }
+ } finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ }
+ $app = $null
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+ if (-not $UseCurrentCache -and -not $CloneHost) {
+ Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10
+ }
+}
diff --git a/corpus/object-tags/restored/cold/source.json b/corpus/object-tags/restored/cold/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..8438449580b9ba3cf9896f9ac2e38a8ea718e747
--- /dev/null
+++ b/corpus/object-tags/restored/cold/source.json
@@ -0,0 +1,14 @@
+[
+ {
+ "path": "Open Notebook.onetoc2",
+ "bytes": 1604,
+ "sha256": "dc9d3f6501503965aa97e94b86766f4e9a87d279eeff4a253ff4612ca8993405",
+ "mtime_ns": 1790822016309856876
+ },
+ {
+ "path": "files.one",
+ "bytes": 67592,
+ "sha256": "fab6f6881081cf1b05cc375301bcfe1e7eda90a294bab3fa137108cf92b45026",
+ "mtime_ns": 1790822016309661293
+ }
+]
diff --git a/corpus/object-tags/restored/cold/teardown.json b/corpus/object-tags/restored/cold/teardown.json
new file mode 100644
index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70
--- /dev/null
+++ b/corpus/object-tags/restored/cold/teardown.json
@@ -0,0 +1 @@
+{"absent": true}
diff --git a/corpus/picture-link/README.md b/corpus/picture-link/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..335bfdf1de4e32c7030d8f9441298bfeffcd29ac
--- /dev/null
+++ b/corpus/picture-link/README.md
@@ -0,0 +1,15 @@
+# Pictures with a link
+
+`lab/` holds screenshots of OneNote 2010 in a disposable clone of
+`corpus/m6/native-features-01`, on the page "Image png", whose picture stores a link
+(WzHyperlinkUrl, the COM read's `hyperlink`). Hovering it shows the address and "Ctrl+click
+to follow link" (`hover.png`); a click selects the picture (`click-selects.png`); Ctrl+click
+opens the address, here an unreachable one (`ctrl-click.png`). Snowbound follows the same
+link on Ctrl+click, Command+click on macOS.
+
+`candidate/` is the output of `a_linked_picture_keeps_its_link_through_undo` in
+`crates/canvas/tests/pictures.rs`: the picture deleted and brought back by undo, published
+as one revision. `cold/` is a fresh OneNote 2010 read, whose picture still links there.
+`tools/test_picture_link.py` checks this without a VM. Regenerate with
+`CANVAS_PICTURE_LINK_EXPORT` set to a new absolute directory while running the test, then
+cold-open it with `tools/native_runner.py OUTPUT COLD --collect-notebook`.
diff --git a/corpus/picture-link/candidate/Features.one b/corpus/picture-link/candidate/Features.one
new file mode 100644
index 0000000000000000000000000000000000000000..2dbc9aa00b26ea9346af118b8654f1456fb78966
Binary files /dev/null and b/corpus/picture-link/candidate/Features.one differ
diff --git a/corpus/picture-link/candidate/Open Notebook.onetoc2 b/corpus/picture-link/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..4c362a1db4fc70a61d84216cbea0ca32e2a4c4ee
Binary files /dev/null and b/corpus/picture-link/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/picture-link/cold/commands.jsonl b/corpus/picture-link/cold/commands.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..c26282d89d832c5548194a40e8890c509a88955d
--- /dev/null
+++ b/corpus/picture-link/cold/commands.jsonl
@@ -0,0 +1,3 @@
+{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-70F8F401 -ExpectedPages -1", "exit": 0, "stdout": "Read 1 sections and 13 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/picture-link/cold/machine.json b/corpus/picture-link/cold/machine.json
new file mode 100644
index 0000000000000000000000000000000000000000..1d030a4409ef7178bdbbff02866e088bd3585bdc
--- /dev/null
+++ b/corpus/picture-link/cold/machine.json
@@ -0,0 +1 @@
+{"name": "m6-70f8f401", "hostname": "ONE-M6-70F8F401"}
diff --git a/corpus/picture-link/cold/notebook/Features.one b/corpus/picture-link/cold/notebook/Features.one
new file mode 100644
index 0000000000000000000000000000000000000000..82d5101f3bf5bb57a6e69ecc221f406867b763d8
Binary files /dev/null and b/corpus/picture-link/cold/notebook/Features.one differ
diff --git a/corpus/picture-link/cold/notebook/Open Notebook.onetoc2 b/corpus/picture-link/cold/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..293d223132041edc706fde5a7fd5be72939a8fb6
Binary files /dev/null and b/corpus/picture-link/cold/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/picture-link/cold/read/4cff0039dbdf53a59f5f4e4592c29a5297ea3b7acce24415c0ae83c36c6bd7c2.attachment b/corpus/picture-link/cold/read/4cff0039dbdf53a59f5f4e4592c29a5297ea3b7acce24415c0ae83c36c6bd7c2.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..a959c053358a06abc072d3492e7a86d2e712d804
Binary files /dev/null and b/corpus/picture-link/cold/read/4cff0039dbdf53a59f5f4e4592c29a5297ea3b7acce24415c0ae83c36c6bd7c2.attachment differ
diff --git a/corpus/picture-link/cold/read/56d4af65701c26df20bd4021eda95b6e830348ce3a746086079fe89285548dc9.attachment b/corpus/picture-link/cold/read/56d4af65701c26df20bd4021eda95b6e830348ce3a746086079fe89285548dc9.attachment
new file mode 100644
index 0000000000000000000000000000000000000000..96260477f0ca60b1605e2971d8cdc146b8b4e02b
Binary files /dev/null and b/corpus/picture-link/cold/read/56d4af65701c26df20bd4021eda95b6e830348ce3a746086079fe89285548dc9.attachment differ
diff --git a/corpus/picture-link/cold/read/environment.json b/corpus/picture-link/cold/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..f1ef64dd3492e1e5aae588ec1afb7a0547e15da4
--- /dev/null
+++ b/corpus/picture-link/cold/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-70F8F401",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/picture-link/cold/read/hierarchy.xml b/corpus/picture-link/cold/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ce8771673d5f06eccd60c12b023577b8c14bf096
--- /dev/null
+++ b/corpus/picture-link/cold/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/picture-link/cold/read/page-000.xml b/corpus/picture-link/cold/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f0561b8658a6ea132fabf360da397a4fd71a2bc6
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-000.xml
@@ -0,0 +1,46 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAMAAADS6oI8AAADAFBMVEW4t5mrt5O4qpWrqo6euo2S
+uoeZs4ieqoiSqoK4nJCrnIq4kIyrkIaen4SSn36ZmH+ekH+SkHmGuoGGs395t3pounJos3CGqnx5
+qnVtqm9iqmqGnHd5nHGGkHN5kG1on2lomGdokGS4hIirhIK4d4Srd32eh3ySh3aZgHeed3eSd3G4
+a4Cra3m4YHyrYHaea3OSa22eYG+SYGmGh3CGgG55hGloh2FogF+Gd2t5d2Rtd15id1mGa2d5a2CG
+YGN5YF1ta1pia1VoYFRWumlWs2dJt2I4ulo4s1hWqmRJql09qlcyqlImulEaukshs0wKukMKs0Em
+qkwaqkYPqkAEqjtWnF9JnFlWkFtJkFU4n1E4mE84kEwmn0gan0IhmEMmkEMakD0KnzoKmDgKkDVW
+h1hWgFZJhFE4h0k4gEdWd1NJd0w9d0Yyd0FWa09Ja0hWYEtJYEU9a0Iyaz04YDwmh0AahzohgDsK
+hzIKgDAmdzsadzUPdy8EdyomazcaazEmYDMaYC0PaysEayYKYCW4VHirVHK4R3SrR22eV2ySV2aZ
+UGeeR2eSR2G4O3CrO2m4MGyrMGaeO2OSO12eMF+SMFmGV2CGUF55VFloV1FoUE+GR1t5R1RtR05i
+R0mGO1d5O1CGMFN5ME1tO0piO0VoMES4JGirJGK4GGSrGF6eJ1ySJ1aZIFeeGFeSGFG4DmGrDlq4
+A12rA1eeDlSSDk6eA1CSA0qGJE95JEmGGEt5GEVoJ0FoID9oGDyGDkh5DkGGA0R5Az5tDjtiDjZo
+AzVWVEdJVEE4Vzk4UDdWR0NJRzw9RzYyRzFWOz9JOzhWMDtJMDU9OzIyOy04MCwmVzAaVyohUCsK
+VyIKUCAmRysaRyUPRx8ERxomOycaOyEmMCMaMB0POxsEOxYKMBVWJDdJJDFWGDNJGC04Jyk4ICc4
+GCRWDjBJDilWAyxJAyY9DiMyDh44Ax0mJyAaJxohIBsmGBsaGBUKJxIKIBAKGA0mDhgaDhImAxQa
+Aw4PDgwEDgcKAwZYdbPqAAAAAWJLR0QAiAUdSAAAAAxjbVBQSkNtcDA3MTIAAAADSABzvAAABjJJ
+REFUWEedkImbTgUcha9ChZl2RUKR0TKTtGiRsZUSEpI0tKm0GoW0WSOjMmNpL82oFG1apJJltNDC
+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
+AElFTkSuQmCC
+
diff --git a/corpus/picture-link/cold/read/page-001.xml b/corpus/picture-link/cold/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..73fe4f193326218fe3a6ff765e0a99551663f335
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-001.xml
@@ -0,0 +1,6 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec
+CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI
+E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp
+oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT
++wHXVRp4qDc0HQAAAABJRU5ErkJggg==
diff --git a/corpus/picture-link/cold/read/page-002.xml b/corpus/picture-link/cold/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2aef42b979042ade456190a5c69f04aea99678f2
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-002.xml
@@ -0,0 +1,6 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec
+CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI
+E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp
+oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT
++wHXVRp4qDc0HQAAAABJRU5ErkJggg==
diff --git a/corpus/picture-link/cold/read/page-003.xml b/corpus/picture-link/cold/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2fd399d83bf4359c150ea3d67830ce2c5545f28e
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-003.xml
@@ -0,0 +1,9 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAO
+xAAADsQBlSsOGwAAARlJREFUeF7t2VFHQ2EcBvD3PWetVqvVam1ERESMiIiIiL7/J5p9gD1dvF51
+8eNcn4uf53n2P1ZLKUOpnkMCtYx0kkAt00F8gkAtsxFQBJpPAEWgxRGgCLScAopAq2NAEWhzAigC
+3c0ARaD7U0AR6OEMUAR6nAOKQE/ngCLQ8wWgCLRdAIpAL5eAItDrFaAI9LYEFIHerwFFoI8bQBHo
+cwUoAn3dAopA32tAEehnAygKjPu/xjyHBAY0WQDQL+0BBKhtYSVIgiSo6w+xiqmYiqnYX34J2SAb
+ZINskA1qa4EESZAEdW1B15e7g9xBbf2VIAmSICPtDmprgQRJkAR1bUHXl7uD3EFt/ZUgCZIgI+0O
+amuBBEnQP07QDo1OW207QPoWAAAAAElFTkSuQmCC
+
diff --git a/corpus/picture-link/cold/read/page-004.xml b/corpus/picture-link/cold/read/page-004.xml
new file mode 100644
index 0000000000000000000000000000000000000000..73a40d4afe578cd8667f5500e8e22f4b8116bf1c
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-004.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/picture-link/cold/read/page-005.xml b/corpus/picture-link/cold/read/page-005.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7299aeae1d5b627b427b32b17c1aec9f3a8d6414
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-005.xml
@@ -0,0 +1,6 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec
+CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI
+E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp
+oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT
++wHXVRp4qDc0HQAAAABJRU5ErkJggg==
diff --git a/corpus/picture-link/cold/read/page-006.xml b/corpus/picture-link/cold/read/page-006.xml
new file mode 100644
index 0000000000000000000000000000000000000000..bdc6ba80634759a819c2742d6ae17158ace2e370
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-006.xml
@@ -0,0 +1,4 @@
+
+Inherited strike and spacing]]>مرحبا بالعالم]]>
diff --git a/corpus/picture-link/cold/read/page-007.xml b/corpus/picture-link/cold/read/page-007.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5898c8243515a9bd549c125458e5a256a0e7d621
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-007.xml
@@ -0,0 +1,6 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec
+CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI
+E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp
+oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT
++wHXVRp4qDc0HQAAAABJRU5ErkJggg==
diff --git a/corpus/picture-link/cold/read/page-008.xml b/corpus/picture-link/cold/read/page-008.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cf2684097540ee215500b4d13fd7680c7a7214bf
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-008.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/picture-link/cold/read/page-009.xml b/corpus/picture-link/cold/read/page-009.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a17dcc41090a8393c2762d39193735a35b620fec
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-009.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/picture-link/cold/read/page-010.xml b/corpus/picture-link/cold/read/page-010.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4a8416f6e32f41ce115a91f21b98bbde0cec24c6
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-010.xml
@@ -0,0 +1,300 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAAXNSR0IArs4c6QAAAARnQU1BAACx
+jwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAEImSURBVHheDbiBV2GL+v+/f9V1q+uk46TjpDFp
+TBqDMUhoh3Zoh3Zoh3Zoh3YYgzE0Jo1J46TjpOOk61Yf32pZ1bKqZVXLqpZV/W2/8y8863nez/v1
+AgAAaAL+vxagiQQ0twIt7cC/yACpA/h3J9BKBdq6gPZu4D90gNwD/NQLdDABSh/Q2Q/8zAaoA8Av
+g0AXF6DxgG4B8KsQoIuA3yRAjxRgyIBeBfACBJhK4KUa6IMAlgbo1wGvYICtB14bgQEE4JiAQQvw
+BgW4VuCtHeBhAN8BCFzAOxwQuoH3XkBEAGIfIAkAQ0FAGgKGI4AsCshjgGIFGEkA4CowugYoU4Bq
+HVBvAGMZANoExrcATQ7QbgO6HWCiAMC7wOQeoC8Bhn3AeAhMlQHkCJg+AUwVwHwKWM6BmSqAXgCz
+V4C1BtiuAfstMFcHsDtg/gFwNADnI+B6BhYAoPn/a2puamluJjW3tDb/q72ZRG7+d0dza2dzG7W5
+vav5P93NZHrzTz3NHb3NFGZzZ1/zz/3NVHbzLwPNXYPNNG5zN6/5V0EzXdj8m6i5R9LMkDb3yppf
+KJqZYPNLZXOfupkFNfdrml/pmtlw82t984CxmYM0D5qa31iauWjzW2szz97Mx5oFjuZ3rmYh3vze
+3SzyNouJZomveSjQLA02D4eaZZFmebRZEWseWWkGE82jq83KtWZVqlm93jy20Qxlmsc3mzVbzdpc
+s267eWKnGS40T+426/eaDaVm437z1GEzUm6ePmo2nTSbK82W0+aZ82a02jx70Wy9arbVmu3XzXO3
+zVi9ef6u2fHQ7Gw0ux6bF56bcQAgNTX9MxtSC4n0r1YSqZ30bzKptYPU1klqp5L+00Uid5N+opM6
+ekiUXlInk/RzH4naT/qFTeoaINEGSd1c0q88El1A+k1I6hGRGBJSr5T0QkZiKkgvQVKfksRSk/oh
+0isNia0jvYZJA3oSx0gaREhvTCSuhfQWJfGsJL6dJMBI7xwkoYv0HieJ3CSxlyQhSEM+kjRAGg6S
+ZCGSPEJSREkjMRK4QhpNkJSrJNUaSZ0ija2ToA3SeIak2SRpt0i6HGlimwTvkCYLJP0uybBHMpZI
+U/sk5JA0XSaZjkjmE5KlQpo5JaHnpNkqyXpBsl2R7DXS3DUJuyXN10mOO5LzgeRqkBYeSfgzaREA
+2pqb2lpa2v5FaiO1tv27va2V3NbW0dbe2fYfahu5q+2n7rYOehulp62zt+1nZhu1r+2X/rYudhtt
+oK17sO1Xbhud1/aboK1H2MYQtfVK2l5I25iytpeKtj6wjaVs61e3vYLa2Jq217q2AbiNo28bNLa9
+Qdq4pra3ljYe2sa3tgnsbe+wNqGj7b2rTYS3id1tEm/bENEm9bUNB9pkwTZ5qE0RaRuJtoGxttGV
+NmWiTbXapl5rG0u1Qett4xttmkybdrNNt9U2kWuDt9smd9r0hTbDbptxr22q1Ibst00ftpnKbeaj
+NstJ20ylDT1tmz1vs1bbbBdt9qu2uVobdt02f9vmqLc579pcD20LjTb8sW3xuc0NAOSWJvK/Wsgk
+EvnfreTWdnIbmdzeQf5PJ5lMJf/URe7oJlPo5M4e8s+9ZCqT/EsfuaufTGOTuwfIvw6S6Vzybzxy
+j4DMEJJ7ReQXEjJTSn4pI/cpyCyQ3K8kv1KT2RD5tYY8oCNzYPKgnvzGSOYi5LcmMs9C5qNkgZX8
+zk4WYuT3DrLIRRbjZImbPOQlSwnysI8sC5DlQbIiRB6JkMEoeTRGVq6QVQmyepU8tkaGUuTxdbJm
+g6zNkHWb5IktMpwjT26T9TtkQ4Fs3CVP7ZGREnl6n2w6JJvLZMsReeaEjFbIs6dk6znZViXbL8hz
+V2SsRp6/Jjtuyc462XVHXngg4w3y4iPZ/Uz2AADlX00UUgvl3yRKayulrZ3STqb8p4NC7qT8RKV0
+dFEo3ZROOuXnHgq1l/ILk9LVR6H1U7rZlF8HKPRBym9cSg+PwhBQeoWUFyIKU0J5KaX0ySgsBaUf
+pLxSUthqymuIMqChcHSUQZjyRk/hGilvEQrPROFbKAKU8s5KEdop7zGKyEERuygSnDLkpki9lGGC
+IvNR5AGKIkgZCVHACGU0SlHGKKoVijpBGVulQGuU8RRFs07RblB0GcrEJgXeokzmKPptimGHYixQ
+pnYpyB5lukQx7VPMhxRLmTJzREFPKLMVivWUYjun2KuUuQsKdkWZr1Ec1xTnLcVVpyzcUfAHymKD
+4n6keJ4pXgCgkpqo/26htpKoba3U9nbqf/7ZnA7qT53UDiqV0kXt7Kb+TKdSe6i/9FK7mFRaH7W7
+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
+B583DmKZg/jmwcrWwZfcQWL74OvOwWrhILl7sLZ38K10kNo/+H54sF4+SB8dbJwc/KgcZE4Pfj8/
+2KweZC8Otq4O/qgd5K4P/rw92K4f5O8Odh4O/mocFB4P/n4+2AWAsqSpPNRSlpLKw61lWXtZTi4r
+OsojnWWQWh7tKiu7yyp6Wd1THustQ8zyeF9Z01/Wssu6gfLEYBnmlid5Zb2gbBCWjaLylKSMSMvT
+srJJUTaDZYuyPKMuo1B5VlO26so2uGzXl+eMZQwpz5vKDkvZiZZd1vKCvYxj5UVH2e0qe/Cy111e
+8pYJorzsK/sCZX+wHAiVP0T+0Tnlj7FyaKUcTpQjq+VPa+Voqvx5vRzbKMcz5ZXN8petciJX/rpd
+Xt0pJwvltd3yt71yqlT+vl9ePyyny+WNo/KPk3KmUv79tLx5Xs5Wy1sX5T+uyrla+c/r8vZtOV8v
+79yV/3ooFxrlvx/Lu8/lIgAcDzUdS1uOh0nHstZjefuxgnw80nEMdh6PUo+VXceq7mM1/Xis5xjq
+PR5nHmv6jrX9xzr28cTAMTx4PMk91vOODYJjo/B4SnSMSI6npccm2bFZcWwBj2eUx6j6eBY6tmqO
+bbpjO3w8pz/GjMfzyLHDdOy0HLvQ4wXrMW4/XsSO3Y5jj+vYix8vuY8J7/EycezzHfsDx4Hg8YfQ
+cTBy/DF6HIodh1eOI4njT6vH0bXjz6nj2PpxfON4JXP8ZfM4sXX8NXe8un2c3DleKxx/2z1O7R1/
+Lx2v7x+nD483ysc/jo4zJ8e/V443T4+z58db1eM/Lo5zV8d/1o63r4/zt8c79eO/7o4LD8d/N453
+H4+Lz8d7AFCRNlWGWyoyUkXeWlG0V0bIFbCjMtpZUVIrqq6KursyRq9APZXx3oqGWdH2VXT9lQl2
+BR6oTA5W9NyKgVcxCipTwgoiqkxLKiZpxSyrWBSVGbCCKiuz6ooVqtg0FbuuMgdXMH1l3lhxIBWn
+qeKyVBbQCm6tLNorbqzicVS8rsoSXiHclWVvxUdU/L5KIFD5EKwEQ5WPkUooWgnHKpGVyqdEJbpa
++bxWiaUq8fXKykblS6aS2Kx83aqs5irJ7craTuVboZLarXzfq6yXKun9ysZh5Ue5kjmq/H5S2axU
+sqeVrfPKH9VK7qLy51Vlu1bJX1d2bit/1SuFu8rfD5XdRqX4WNl7rvwXAM6Gm85kLWdy0pmi9Wyk
+/Qwkn412nCk7z1TUM3XX2Vj3GUQ/G+850/SeaZlnur6zif4zmH02OXCmHzwzcM+MvLMpwRkiPJsW
+nZkkZ2bpmUV2NqM4Q8GzWeWZVX1mg87smrM53RkGn83rzxzGMydy5jKdLVjOcPRs0Xrmtp95sDOv
+42zJdUbgZ8vuM5/3zE+cBXxnHwJnweDZx9BZKHIWjp5FYmefVs6iibPPq2extbN46mxl/ezLxlki
+c/Z182x16yyZO1vbPvu2c5YqnH3fPVvfO0uXzjb2z34cnmXKZ78fnW2enGUrZ1unZ3+cn+WqZ39e
+nG1fneVrZzvXZ3/dnhXqZ3/fne0+nBUbZ3uPZ/99PisBQFXWVJW3VBWk6khrFWyvjpKryo6qqrOq
+plbHuqpQd3WcXtX0VLW9VR2zOtFXhfurk+yqfqBqGKwaudUpXhURVKeFVZOoapZULdLqjKyKKqqz
+YNWqrNrUVTtUndNUMV11Hq469FWnsepCqgumKm6pLqJVt7XqsVe9WHXJUSVc1WW86nNX/d5qgKh+
+8FWDgerHYDUUqoYj1Ui0+ilWja5UPyeqsX9k11p1JVX9sl5NbFS/Zqqrm9XkVnUtV/22XU3tVL8X
+quu71fRedaNU/bFfzRxWfy9XN4+q2ZPqVqX6x2k1d179s1rdvqjmr6o7tepf19XCbfXvenX3rlp8
+qO41qv99rJaeq/8DgEt506Wi5XKEdAm2Xo62XyrJl6qOS3Xn5Rj1Euq6HO++1NAvtT2Xut7LCeYl
+3Hc52X+pZ18aBi6Ng5dT3EuEdzktuDQJL82iS4vkckZ6icouZxWXVvDSpry0qy/noEtMczmvu3TA
+l079pct4uYBc4qbLRculG730WC+99ssl7JJwXC67Ln34pd99GfBefiAug77Lj4HLUPAyHLqMRC4/
+RS+jscvPK5exxGV89XJl7fJL6jKxfvl143I1c5ncvFzbuvyWu0xtX37fuVwvXKZ3Lzf2Ln+ULjP7
+l78fXm6WL7NHl1snl39ULnOnl3+eX25XL/MXlztXl3/VLgvXl3/fXu7WL4t3l3sPl/9tXJYeL//3
+fLkPADVFU22kpQaSaqOtNWV7TUWuqTtqY501iFob76ppumtaek3XU5vorcHM2mRfTd9fM7BrxoHa
+1GAN4dameTWToGYW1iyi2oykhkprs7KaVVGzgTW7sjanrmFQbV5Tc+hqTrjm0tcWjDUcqS2aam5L
+zYPWvNbakr1GYLVlR83nqvnxWsBd++CtBYnaR18tFKiFg7VIqPYpUotGa59jtdhKLZ6orazWvqzV
+/lGBX9drqxu1ZKa2tln7tlVL5Wrft2vrO7V0obaxW/uxV8uUar/v1zYPa9lybeuo9sdJLVep/Xla
+2z6v5au1nYvaX1e1Qq3293Vt97ZWrNf27mr/faiVGrX/Pdb2n2sHAHAz0nQDttyMkm6UrTeq9hs1
++Was4wbqvBmn3mi6brTdNzr6zUTPDdx7M8m80ffdGPpvjOybqYEbZPBmmntj4t2YBTcW4c2M6AaV
+3MxKb6yyG5vixg7ezClvMPXNPHTj0Nw4dTcu+GZBf4MbbxaRG7fpxmO58aI3S9Ybwn6zjN34HDd+
+100Av/ngvgl6bz4SNyHfTThwEwnefArdRCM3n6M3sdhNfOVmJXHzZfUmsXbzNXWzun6T3LhZy9x8
+27xJbd18z92sb9+kd242Cjc/dm8yeze/l24292+yhzdb5Zs/jm5yJzd/Vm62T2/y5zc71Zu/Lm4K
+Vzd/1252r2+Ktzd79Zv/3t2UHm7+17jZf7w5eL45BIA62FQfbakrSXVVa13dXh8j16GO+nhnXUOt
+a7vquu76BL0O99Qne+t6Zt3QVzf216fYdWSgPj1YN3HrZl7dIqjPCOuoqD4rqVuldZusblfU58A6
+pqzPq+sOqO7U1F26+gJcx/X1RWPdjdQ9prrXUl9C64S1vmyv+7C631EPuOof8HrQXf/orYeIethX
+jwTqn4L1aKj+OVKPRevxWH1lpf4lUU+s1r+u1VdT9eR6fW2j/i1TT23Wv2/V13P19HZ9Y6f+o1DP
+7NZ/36tvlurZ/frWYf2Pcj13VP/zpL5dqedP6zvn9b+q9cJF/e+r+m6tXryu793W/1uvl+7q/3uo
+7zfqB4/1w+f6/wHA/WjTvbLlXkW6V7fej7XfQ+T78Y57Tee9lnqv67qf6L6H6feTPff63nsD897Y
+dz/Vf4+w76cH7k2D92buvYV3PyO4R4X3s6J7q+TeJr23y+7nFPcYeD+vvHeo753QvUtzv6C7x+H7
+Rf2923jvQe69pvslyz2B3i9b7332ez92H3Dcf3DdB/H7j+77kPc+TNxHfPefAvfR4P3n0H0sch+P
+3q/E7r+s3CcS919X71fX7pOp+7X1+28b96nM/ffN+/Wt+3TufmP7/sfOfaZw//vu/ebefbZ0v7V/
+/8fhfa58/+fR/fbJfb5yv3N6/9f5faF6//fF/e7VfbF2v3d9/9/b+1L9/n939/sP9weN+8PH+/97
+vi8DQEPZ1FC1NNSkxlhrA2pvjJMbmo6GtrOhozYmuhpwd2OS3tD3NAy9DSOzMdXXQPob0+yGaaBh
+HmxYuI0ZXgMVNGaFDauoYZM07NLGnKyBKRrzYMOhbDjVDRfUWNA0cF1jEW649Q2PseFFGkumBmFp
+LKMNn7XhtzcCWOODoxF0NT7ijZC7EfY2IkTjk68RDTQ+BxuxUCMeaaxEG19ijcRK42uisbraSK41
+1lKNb+uN1Ebje6axvvmPJW1s5Bo/thuZncbvhcbmbiO719gqNf7Yb+QOG3+WG9tHjfxJY6fS+Ou0
+UThv/F1t7F40ileNvVrjv9eN0m3jf/XG/l3j4KFx2Gj832Oj/Nz4fwDwpGp6Urc8jZGeoNan8fYn
+DflJ2/Gk63yaoD7BXU+T3U96+pOh58nY+zTFfEL6nqb7n0zsJ/PAk2XwaYb7hPKeZgVPVuGTTfRk
+lzzNSZ8w2dO84skBPjmVTy710wL0hGueFnVPbvjJo3/yGp+WkCfC9LRsefKhT37rU8D+9AF7Cjqe
+PrqeQvhT2P0U8T59Ip6ivqfPgadY8CkeelqJPH2JPiViT19XnlYTT8nVp7W1p2+pp9T60/eNp/XM
+U3rzaWPr6UfuKbP99PvO02bhKbv7tLX39EfpKbf/9Ofh03b5KX/0tHPy9FflqXD69Pf50271qXjx
+tHf19N/aU+n66X+3T/v1p4O7p8OHp/9rPJUfn/7f89PR/w8QCy14LJdV2gAAAABJRU5ErkJggg==
diff --git a/corpus/picture-link/cold/read/page-011.xml b/corpus/picture-link/cold/read/page-011.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0dbeb78e8136843b048456d4579c2e500f293069
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-011.xml
@@ -0,0 +1,21 @@
+
+/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a
+HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy
+MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABAAGADASIA
+AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA
+AAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3
+ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWm
+p6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEA
+AwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSEx
+BhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElK
+U1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3
+uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDwhYfa
+plh9qsrD7VMsPtUqRFOqVVh9qmWH2qysPtUyw+1UpHbTqlZYfaplh9qsrD7VMsPtVqR206pVWGpl
+h9qsrDUyw+1UpHbTqlZYalWH2q0sNSrD7VSkdtOqVlh9qmWH2qysPtUyw+1UpHdTqnHrD7VMsPtV
+lYfaplh9q81SPyqnVKqw+1TLD7VZWH2qZYfaqUjtp1SssPtUyw+1WVh9qmWH2qlI7adUqrDUyw+1
+WVhqZYfarUjtp1SssNSrD7VaWGpVh9qpSO2nVKyw+1TLD7VZWH2qZYfaqUjup1Tj1h9qmWH2qysP
+tUyw+1eapH5VTqlVYfaplh9qsrD7VMsPtVKR206pWWH2qZYfarKw+1TLD7VSkdtOqVVhqZYfarKw
+1MsPtVqR206pWWGpVh9qtLDUqw+1UpHbTqlZYfaplh9qsrD7VMsPtVKR206px6w+1TLD7VZWH2qZ
+YfavNUj8rp1SqsPtUyw+1WVh9qmWH2qlI7adUrLD7VMsPtVlYfaplh9qpSO2nVKqw1MsPtVlYamW
+H2q1I7adUrLDUqw+1WlhqVYfaqUjtp1SssPtUyw+1WVh9qmWH2qlI7adU//Z
+
diff --git a/corpus/picture-link/cold/read/page-012.xml b/corpus/picture-link/cold/read/page-012.xml
new file mode 100644
index 0000000000000000000000000000000000000000..aa928b73f9cc37cec4b61721eddafabca2aea400
--- /dev/null
+++ b/corpus/picture-link/cold/read/page-012.xml
@@ -0,0 +1,6 @@
+
+iVBORw0KGgoAAAANSUhEUgAAAGAAAABACAIAAABqVuVZAAAAwUlEQVR4nO3ZwQ2DQBDF0Hkk/dec
+CuASwXCwtBVY/mYBM3OMznEC4TsfMx1nEALk2o8ACZB/GpJBAiSD3PcgbmICJIM0MVvX/RokQDJI
+E1ODvPOrS5EWIBmkialB1ntcpAVIBmli1rvTRVGAZJAmZj1AvawKkAzSxNQg6zEu0gIkgzQx69Hp
+oihAMkgT85LTn1UBkkGamBpkvcdFWoBkkCZmvTtdFAVIBmli1gPUy6oAySBNTA2yHuMiLUAyyPMT
++wHXVRp4qDc0HQAAAABJRU5ErkJggg==
diff --git a/corpus/picture-link/cold/read/payloads.json b/corpus/picture-link/cold/read/payloads.json
new file mode 100644
index 0000000000000000000000000000000000000000..206003e10fb990670c5a5c23b760886be2259278
--- /dev/null
+++ b/corpus/picture-link/cold/read/payloads.json
@@ -0,0 +1,18 @@
+[
+ {
+ "sha256": "4cff0039dbdf53a59f5f4e4592c29a5297ea3b7acce24415c0ae83c36c6bd7c2",
+ "name": "Fictitious attachment.txt",
+ "object": "{8FA38498-A15A-08DC-3759-A172FAAA260D}{40}{B0}",
+ "kind": "InsertedFile",
+ "page": "{95B9BD1C-79B9-0611-2445-33C3814DE07A}{1}{B0}",
+ "bytes": 27
+ },
+ {
+ "sha256": "56d4af65701c26df20bd4021eda95b6e830348ce3a746086079fe89285548dc9",
+ "name": "silence.wav",
+ "object": "{8FA38498-A15A-08DC-3759-A172FAAA260D}{44}{B0}",
+ "kind": "MediaFile",
+ "page": "{95B9BD1C-79B9-0611-2445-33C3814DE07A}{1}{B0}",
+ "bytes": 16044
+ }
+]
\ No newline at end of file
diff --git a/corpus/picture-link/cold/run.json b/corpus/picture-link/cold/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..e384f9aedf79668a780b1b05ae2cfe70d6a8d9e0
--- /dev/null
+++ b/corpus/picture-link/cold/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/private/tmp/linklab/candidate",
+ "expected_pages": -1,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35"
+ }
+}
diff --git a/corpus/picture-link/cold/scripts/cold.ps1 b/corpus/picture-link/cold/scripts/cold.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f
--- /dev/null
+++ b/corpus/picture-link/cold/scripts/cold.ps1
@@ -0,0 +1,27 @@
+param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '')
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' }
+$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote'
+if ($CloneHost) {
+ if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) {
+ throw 'The disposable clone hostname does not match this machine.'
+ }
+ New-Item "$key\Options\Paths" -Force | Out-Null
+ New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null
+} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or
+ -not (Test-Path 'C:\one-tests\profile-original-cache')) {
+ throw 'Park the personal OneNote profile before resetting the test cache.'
+}
+$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0'
+$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root))
+if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' }
+New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null
+if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked }
+if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse }
+New-Item "$key\OpenNotebooks" | Out-Null
+New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null
diff --git a/corpus/picture-link/cold/scripts/read.ps1 b/corpus/picture-link/cold/scripts/read.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc
--- /dev/null
+++ b/corpus/picture-link/cold/scripts/read.ps1
@@ -0,0 +1,144 @@
+param(
+ [Parameter(Mandatory=$true)][string]$Root,
+ [int]$ExpectedPages = -1,
+ [switch]$UseCurrentCache,
+ [switch]$Pdf,
+ [switch]$KeepOpen,
+ [string]$CloneHost = ''
+)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+$notebook = Join-Path $root 'notebook'
+$output = Join-Path $root 'read'
+if (Test-Path $output) { throw 'Choose a new read destination.' }
+if ($UseCurrentCache) {
+ if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') {
+ throw 'Park the personal OneNote profile before reading test notebooks.'
+ }
+} else {
+ & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost
+}
+New-Item -ItemType Directory -Path $output | Out-Null
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+$failure = $null
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $process = Get-Process ONENOTE
+ @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion;
+ powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } |
+ ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8
+ $sections = @()
+ foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) {
+ $id = ''
+ $app.OpenHierarchy($file.FullName, '', [ref]$id, 0)
+ $sections += $id
+ }
+ $deadline = [DateTime]::UtcNow.AddSeconds(300)
+ $previous = ''
+ $lastChange = ''
+ $stableSince = [DateTime]::UtcNow
+ $settled = $false
+ do {
+ $pages = @{}
+ foreach ($section in $sections) {
+ $hierarchy = ''
+ $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
+ [xml]$xml = $hierarchy
+ foreach ($node in $xml.SelectNodes('//*[@path]')) {
+ if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'OneNote opened a section outside the copied notebook.'
+ }
+ }
+ foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) {
+ $id = $node.GetAttribute('ID')
+ $content = ''
+ $app.GetPageContent($id, [ref]$content, 1, 1)
+ $pages[$id] = $content
+ }
+ }
+ $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] }))
+ if ($signature -ne $previous) {
+ $lastChange = $previous
+ $previous = $signature
+ $stableSince = [DateTime]::UtcNow
+ }
+ if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or
+ ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and
+ ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break }
+ Start-Sleep -Milliseconds 250
+ } while ([DateTime]::UtcNow -lt $deadline)
+ if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) {
+ [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8)
+ $index = 0
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ $index++
+ }
+ $hierarchy = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1)
+ [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8)
+ throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled."
+ }
+ $index = 0
+ $payloads = @()
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ if ($Pdf) {
+ $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index)
+ $app.NavigateTo($id, '', $false)
+ $app.Publish($id, $pdfPath, 3, '')
+ if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' }
+ }
+ [xml]$page = $pages[$id]
+ foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) {
+ $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache'))
+ $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant()
+ [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes)
+ # A paragraph's file is named by its paragraph, one on the page by itself.
+ $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode }
+ $payloads += @{ page = $id; object = $owner.GetAttribute('objectID');
+ kind = $file.LocalName; name = $file.GetAttribute('preferredName');
+ sha256 = $hash; bytes = $bytes.Length }
+ }
+ $index++
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8)
+ $all = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$all, 1)
+ [xml]$finalTree = $all
+ $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique)
+ if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) {
+ throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.'
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8)
+ Write-Output "Read $($sections.Count) sections and $($pages.Count) pages."
+} catch {
+ $failure = $_
+ [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ throw
+} finally {
+ try {
+ try {
+ if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) }
+ if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) {
+ $app.CloseNotebook($notebookId, $false)
+ }
+ } catch {
+ if ($null -eq $failure) { throw }
+ [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ }
+ } finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ }
+ $app = $null
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+ if (-not $UseCurrentCache -and -not $CloneHost) {
+ Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10
+ }
+}
diff --git a/corpus/picture-link/cold/source.json b/corpus/picture-link/cold/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..7fd8f82384e6661feee4a47d4dcf5c1bf00e4c08
--- /dev/null
+++ b/corpus/picture-link/cold/source.json
@@ -0,0 +1,14 @@
+[
+ {
+ "path": "Features.one",
+ "bytes": 123080,
+ "sha256": "d1cc5e4deb98245d742488199c676ad1a44edc808a219d9bd9d1856d5b2ccac0",
+ "mtime_ns": 1790809064276439170
+ },
+ {
+ "path": "Open Notebook.onetoc2",
+ "bytes": 1604,
+ "sha256": "be341d46f14d6242c0e2933e88941f157d2071df895730d01be03156592a808c",
+ "mtime_ns": 1790809064276690168
+ }
+]
diff --git a/corpus/picture-link/cold/teardown.json b/corpus/picture-link/cold/teardown.json
new file mode 100644
index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70
--- /dev/null
+++ b/corpus/picture-link/cold/teardown.json
@@ -0,0 +1 @@
+{"absent": true}
diff --git a/corpus/picture-link/lab/click-selects.png b/corpus/picture-link/lab/click-selects.png
new file mode 100644
index 0000000000000000000000000000000000000000..9d8b011edef2d65056b830ae81f169f62e9ba9b8
Binary files /dev/null and b/corpus/picture-link/lab/click-selects.png differ
diff --git a/corpus/picture-link/lab/ctrl-click.png b/corpus/picture-link/lab/ctrl-click.png
new file mode 100644
index 0000000000000000000000000000000000000000..b7af962ec0de4d3dbd7c148ef46dfaebeea5ef8d
Binary files /dev/null and b/corpus/picture-link/lab/ctrl-click.png differ
diff --git a/corpus/picture-link/lab/hover.png b/corpus/picture-link/lab/hover.png
new file mode 100644
index 0000000000000000000000000000000000000000..33372975d211a4546fb219370207dc3346fd5278
Binary files /dev/null and b/corpus/picture-link/lab/hover.png differ
diff --git a/corpus/printout/README.md b/corpus/printout/README.md
index b04d364e2740906d9cf5cd24acf1ad755fb7ad2e..4c67098f9f7418eff887b0c9a3d5b8ca13451095 100644
--- a/corpus/printout/README.md
+++ b/corpus/printout/README.md
@@ -11,9 +11,13 @@ frames printout pages.
`candidate/` is the output of `printouts_show_onenotes_rendering_and_move_as_pictures`
in `crates/canvas/tests/printouts.rs`: one printout page moved down, the other deleted
and the deletion undone, and text typed before the citation, published as one revision.
-Snowbound does not write XPS packages, so the undone page returns as the PNG OneNote
-showed of it. `cold/` is a fresh OneNote 2010 read: the moved page is still a printout,
-the restored one a picture. `tools/test_printout.py` checks this without a VM.
+The undone page is written back as it was read: its XPS package as the PictureContainer,
+under the file-data type OneNote gives XPS packages (0x8003A), the PNG beside it as the
+WebPictureContainer14, IsPrintout, its page number, and the undocumented properties tying
+it to its package, kept as stored; without that type or those properties OneNote reads
+the page back but draws nothing there. `cold/` is a fresh OneNote 2010 read (with its PDF
+export): both pages are printouts and both draw, the moved one lower down; the restored
+page's package is a copy, which OneNote counts as a second (`xpsFileIndex="1"`). `tools/test_printout.py` checks this without a VM.
Regenerate with `SNOWBOUND_PRINTOUT_EXPORT` set to a new absolute directory while
running the test, then cold-open it with
`tools/native_runner.py OUTPUT COLD --expected-pages 2 --collect-notebook --screenshots`.
diff --git a/corpus/printout/candidate/Open Notebook.onetoc2 b/corpus/printout/candidate/Open Notebook.onetoc2
index 90da353c9de9888642b5d42f22eb53e74962833d..aa0d13e395fee99241c070b9aa4de239840b1477 100644
Binary files a/corpus/printout/candidate/Open Notebook.onetoc2 and b/corpus/printout/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/printout/candidate/Printouts.one b/corpus/printout/candidate/Printouts.one
index d2e66594be0d9f0b2ec85053feb723d819eabd7b..1dc30e24c8fb8bc385378dbfc7a2d978bed53ccf 100644
Binary files a/corpus/printout/candidate/Printouts.one and b/corpus/printout/candidate/Printouts.one differ
diff --git a/corpus/printout/cold/commands.jsonl b/corpus/printout/cold/commands.jsonl
index 06aa4959935e4e5b9720e0342b9fe633deb3d447..f0a3fed7bc5d9bab6e0c7618328652faf143eacf 100644
--- a/corpus/printout/cold/commands.jsonl
+++ b/corpus/printout/cold/commands.jsonl
@@ -1,3 +1,3 @@
{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
-{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-B402FE00 -ExpectedPages 2 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 2 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-02DA9A86 -ExpectedPages 2 -Pdf -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 2 pages.\r\n", "stderr": "", "error": null}
{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/printout/cold/machine.json b/corpus/printout/cold/machine.json
index 41d3b5748eadd4f55fb2f0f51ffb9e06b59334b5..788d866dc93520912f84fc593afe1ef247df1c43 100644
--- a/corpus/printout/cold/machine.json
+++ b/corpus/printout/cold/machine.json
@@ -1 +1 @@
-{"name": "m6-b402fe00", "hostname": "ONE-M6-B402FE00"}
+{"name": "m6-02da9a86", "hostname": "ONE-M6-02DA9A86"}
diff --git a/corpus/printout/cold/notebook/Open Notebook.onetoc2 b/corpus/printout/cold/notebook/Open Notebook.onetoc2
index e052f7f02ea4094221dca80760b88f7952848e6b..06f6f9ff0096ce4a43d1692fefec85a84c6e2e7a 100644
Binary files a/corpus/printout/cold/notebook/Open Notebook.onetoc2 and b/corpus/printout/cold/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/printout/cold/notebook/Printouts.one b/corpus/printout/cold/notebook/Printouts.one
index 8c35ff854862c80e3fc852dfdbcb9bc452ed76d1..1771d7d482b0e1c65b0c8c3bc3ba70c45a037335 100644
Binary files a/corpus/printout/cold/notebook/Printouts.one and b/corpus/printout/cold/notebook/Printouts.one differ
diff --git a/corpus/printout/cold/read/environment.json b/corpus/printout/cold/read/environment.json
index b60b7a9a6d1208e3110d998f20a286622b5034bc..2fca9bd584b1768eab2fc70c2e870314639b6724 100644
--- a/corpus/printout/cold/read/environment.json
+++ b/corpus/printout/cold/read/environment.json
@@ -1,7 +1,7 @@
{
"powershell": "5.1.14409.1005",
"schema": "xs2010",
- "hostname": "ONE-M6-B402FE00",
+ "hostname": "ONE-M6-02DA9A86",
"cold": true,
"onenote": "14.0.4763.1000"
}
diff --git a/corpus/printout/cold/read/hierarchy.xml b/corpus/printout/cold/read/hierarchy.xml
index c8ca7950adf00d326e81c9763e6b67748cc78a5d..34becafb75b05530eb705dcb923afa61a0fe4fca 100644
--- a/corpus/printout/cold/read/hierarchy.xml
+++ b/corpus/printout/cold/read/hierarchy.xml
@@ -1,2 +1,2 @@
-
+
diff --git a/corpus/printout/cold/read/page-000.navigation.json b/corpus/printout/cold/read/page-000.navigation.json
index 61f3843fae261a8867978a4dbc42f1463a422ab2..86a0d5f7e25a7ab73646345e7d3471637334f39e 100644
--- a/corpus/printout/cold/read/page-000.navigation.json
+++ b/corpus/printout/cold/read/page-000.navigation.json
@@ -6,7 +6,7 @@
"h": 600,
"error": null,
"win": {
- "title": "Pasted shape - Microsoft OneNote",
+ "title": "Printout - Microsoft OneNote",
"class": "Framework::CFrame",
"dialog": false
}
diff --git a/corpus/printout/cold/read/page-000.pdf b/corpus/printout/cold/read/page-000.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..34af7b58708f24feb76c9db05bcce6c5dc308944
Binary files /dev/null and b/corpus/printout/cold/read/page-000.pdf differ
diff --git a/corpus/printout/cold/read/page-000.png b/corpus/printout/cold/read/page-000.png
index 713354cb41d1fe4527d207f8f6ae13bb71d379ce..d3cb2874926d3f31b90e675be86f353c04b84ea3 100644
Binary files a/corpus/printout/cold/read/page-000.png and b/corpus/printout/cold/read/page-000.png differ
diff --git a/corpus/printout/cold/read/page-000.xml b/corpus/printout/cold/read/page-000.xml
index e048f48128f2ab49aad3817203b548ebc69f12cf..d0bb55a39af938921d9a469f018e89b2cf438cc1 100644
--- a/corpus/printout/cold/read/page-000.xml
+++ b/corpus/printout/cold/read/page-000.xml
@@ -1,74 +1,526 @@
-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
+file://C:\work\printout\inputs\two-pages.docx>]]>iVBORw0KGgoAAAANSUhEUgAAAzAAAAQgCAIAAAC2Gy5ZAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAO
+xAAADsQBlSsOGwAANTtJREFUeF7t1sENACAMAzFg/51bMcV93AUiOX3kzsxxBAgQIECAAAECncDr
+oiUTIECAAAECBAh8AYPMHxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBP
+gAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECA
+AAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwA
+AQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAAB
+AgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI
+4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAEC
+BAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIE
+DDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQI
+ECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQ
+iAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsL
+EE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAg
+QIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI
+/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECA
+AAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAW
+MMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8
+AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAAB
+AgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMD
+BAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIE
+CBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAg
+iwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQI
+ECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQ
+MMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAg
+QIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBA
+IBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywu
+QDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECA
+AAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg
+8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAAB
+AgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFY
+wCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADx
+BAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIE
+CBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wP
+ECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQ
+IEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGD
+LC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMg
+QIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBA
+wCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CA
+AAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAA
+gVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4
+APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAAB
+AgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGD
+zA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIE
+CBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRi
+AYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLE
+EyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQ
+IEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/
+QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBA
+gACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUM
+srgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+A
+AAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAA
+AYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AAB
+AgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAEC
+BGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMji
+AsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIE
+CBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQM
+Mj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQ
+IECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCI
+BQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQ
+T4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBA
+gAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8
+AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAA
+AQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYw
+yOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwB
+AgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAEC
+BAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wME
+CBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQI
+EIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCL
+CxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQ
+IECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAw
+yPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBA
+gAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAg
+FjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5A
+PAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAA
+AQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDz
+AwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAEC
+BAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjA
+IIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEE
+CBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQI
+EDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8Q
+IECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAg
+QCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMs
+LkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBA
+gAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDA
+IPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAA
+AQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACB
+WMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA
+8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAEC
+BAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPM
+DxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQI
+ECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIB
+gywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQT
+IECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAg
+QMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9A
+gAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECA
+AIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyy
+uADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AA
+AQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAAB
+g8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAEC
+BAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIE
+YgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOIC
+xBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQI
+ECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwy
+P0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAg
+QIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgF
+DLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBP
+gAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECA
+AAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwA
+AQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAAB
+AgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI
+4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAEC
+BAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIE
+DDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQI
+ECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQ
+iAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsL
+EE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAg
+QIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI
+/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECA
+AAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAW
+MMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8
+AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAAB
+AgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMD
+BAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIE
+CBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAg
+iwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQI
+ECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQ
+MMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAg
+QIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBA
+IBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywu
+QDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECA
+AAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg
+8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAAB
+AgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFY
+wCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADx
+BAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIE
+CBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wP
+ECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQ
+IEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGD
+LC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMg
+QIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBA
+wCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CA
+AAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAA
+gVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4
+APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAAB
+AgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGD
+zA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIE
+CBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRi
+AYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLE
+EyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQ
+IEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/
+QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBA
+gACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUM
+srgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+A
+AAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAA
+AYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AAB
+AgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAEC
+BGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMji
+AsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIE
+CBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQM
+Mj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQ
+IECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCI
+BQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQ
+T4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBA
+gAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8
+AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAA
+AQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYw
+yOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwB
+AgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAEC
+BAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wME
+CBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQI
+EIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCL
+CxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQ
+IECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAw
+yPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBA
+gAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAg
+FjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5A
+PAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAA
+AQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDz
+AwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAEC
+BAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjA
+IIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEE
+CBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQI
+EDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8Q
+IECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAg
+QCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMs
+LkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBA
+gAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDA
+IPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAA
+AQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACB
+WMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA
+8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAEC
+BAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPM
+DxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQI
+ECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIB
+gywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQT
+IECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAg
+QMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9A
+gAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECA
+AIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyy
+uADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AA
+AQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAAB
+g8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAEC
+BAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIE
+YgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOIC
+xBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQI
+ECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwy
+P0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAg
+QIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgF
+DLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBP
+gAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECA
+AAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwA
+AQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAAB
+AgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI
+4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAEC
+BAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIE
+DDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQI
+ECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQ
+iAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsL
+EE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAg
+QIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI
+/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECA
+AAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAW
+MMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8
+AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAAB
+AgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMD
+BAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIE
+CBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAg
+iwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQI
+ECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQ
+MMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAg
+QIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBA
+IBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywu
+QDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECA
+AAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg
+8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAAB
+AgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFY
+wCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADx
+BAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIE
+CBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wP
+ECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQ
+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
+b7w9fTfueZOd73/e+no85cQ4n3a+6vxw83zPK8yV7fpH5xHPfP71T7///e9/8IMffLyR9vS5j5BP
+b/zDH/7wz3me+eaP9+SennzFOH+ueM9QPP3M/8l3H1+8dZ9BgAABAgT+JwXOOyX+vLDAxz/+8Ve8
+4hV//vOfn37aq171qp/85CfnI+fj5++//e1vz9/PKvrqV796fdr54PnPM27O1/7pT386fz+fc/5+
+febXvva1Z/7++Krr216f/NGPfvSZbOeD58/1wfNNTrbHJ5//en38RHo89Ap/BTh/vz7/+vv1oGdO
+uJ57Hfs457rxvCyfCfP0m5/vdgU7u+rxlEfC88FH7Mejrw9e2R5f/swj/FcCBAgQILAg4B2yF1+/
+3/72t7/yla+88Y1vfPp+0nnb7AMf+MD54te97nVf+tKXfv7zn5+3gn7wgx987GMfe/pe2je/+c3z
+487Xv/7154PnZ4gf+chHzmden/DlL3/5+qni+973vvPrWefLz9tUj297/tF56POG+/Wvf319/Hzh
+eaPregvqfIdPf/rTz/v5jwCf+tSnzuf/8+ecEfahD33oCnOF/M1vfvP0nHPjj370oxf+5u94xzvO
+J5yNdX5gesSuT37zm998PfGsrg9/+MPXB9/73vde77cdioNw4VyY3iR7XmQfJECAAIGXvYBB9pIq
+Plvn/FbWWTbvfOc7z2g48+IMoMdP3z73uc+d8f7Xv/71n38H/0y0pw84W+R85jOPfO1rX3u9e3R9
+25cU6F8/6Xzh+c9//ob/1nc4P3h93u9/5tEPf/jDxznXhHrec14g2xltj3/6+Onk+ano9cSzus4j
+rk/4whe+cIba+csvfvGLz3/+84/nvvTDfSYBAgQIEHiZCRhk/45Cr3dxzmy63tS5fs3/+vOZz3zm
+Na95zT9/r8fbQtc/+uUvf/kCz7u+7Uv/czbTS//kF/jMN73pTSfn03POpc97zgt8k8fvip1fC/vs
+Zz97fbczYa8vOT/0fEzY827cJfme97znvEP29Lkv/C8i/Jcc65sQIECAAIGggEH2IqWcefH4nf3z
+I8Xz2W9/+9uv97Se+Z+NuN4i+u53v3t9x/OL8+c/z1ti512i6yPny7/3ve+9//3v/7ce+fTbnk8+
+b7w972eed+muj3/xi188Q+rfO+Me3/Pxe/rnnbPz9tUzPy58es4ZW2dFPW+Yc901xa7Dz6I63/Yx
+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
diff --git a/corpus/printout/cold/read/page-001.navigation.json b/corpus/printout/cold/read/page-001.navigation.json
index 86a0d5f7e25a7ab73646345e7d3471637334f39e..61f3843fae261a8867978a4dbc42f1463a422ab2 100644
--- a/corpus/printout/cold/read/page-001.navigation.json
+++ b/corpus/printout/cold/read/page-001.navigation.json
@@ -6,7 +6,7 @@
"h": 600,
"error": null,
"win": {
- "title": "Printout - Microsoft OneNote",
+ "title": "Pasted shape - Microsoft OneNote",
"class": "Framework::CFrame",
"dialog": false
}
diff --git a/corpus/printout/cold/read/page-001.pdf b/corpus/printout/cold/read/page-001.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..42949e1e0e1fad0e232632f9a995c839bd46ed76
Binary files /dev/null and b/corpus/printout/cold/read/page-001.pdf differ
diff --git a/corpus/printout/cold/read/page-001.png b/corpus/printout/cold/read/page-001.png
index 5865baf7c33b8ee492af2900abb11b5dd99a4a30..65c62a11e38681ee5382adcbb738a8b02b620fae 100644
Binary files a/corpus/printout/cold/read/page-001.png and b/corpus/printout/cold/read/page-001.png differ
diff --git a/corpus/printout/cold/read/page-001.xml b/corpus/printout/cold/read/page-001.xml
index fcdc47760913a58a9bf0c0d30b661a0b1fd0d0af..69cf8a35bf8af36f728f9b46fa61d554d65a6108 100644
--- a/corpus/printout/cold/read/page-001.xml
+++ b/corpus/printout/cold/read/page-001.xml
@@ -1,526 +1,74 @@
-file://C:\work\printout\inputs\two-pages.docx>]]>iVBORw0KGgoAAAANSUhEUgAAAzAAAAQgCAIAAAC2Gy5ZAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAO
-xAAADsQBlSsOGwAANTtJREFUeF7t1sENACAMAzFg/51bMcV93AUiOX3kzsxxBAgQIECAAAECncDr
-oiUTIECAAAECBAh8AYPMHxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBP
-gAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECA
-AAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwA
-AQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAAB
-AgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI
-4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAEC
-BAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIE
-DDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQI
-ECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQ
-iAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsL
-EE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAg
-QIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI
-/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECA
-AAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAW
-MMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8
-AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAAB
-AgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMD
-BAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIE
-CBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAg
-iwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQI
-ECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQ
-MMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAg
-QIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBA
-IBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywu
-QDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECA
-AAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg
-8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAAB
-AgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFY
-wCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADx
-BAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIE
-CBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wP
-ECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQ
-IEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGD
-LC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMg
-QIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBA
-wCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CA
-AAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAA
-gVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4
-APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAAB
-AgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGD
-zA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIE
-CBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRi
-AYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLE
-EyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQ
-IEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/
-QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBA
-gACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUM
-srgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+A
-AAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAA
-AYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AAB
-AgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAEC
-BGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMji
-AsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIE
-CBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQM
-Mj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQ
-IECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCI
-BQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQ
-T4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBA
-gAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8
-AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAA
-AQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYw
-yOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwB
-AgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAEC
-BAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wME
-CBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQI
-EIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCL
-CxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQ
-IECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAw
-yPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBA
-gAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAg
-FjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5A
-PAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAA
-AQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDz
-AwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAEC
-BAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjA
-IIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEE
-CBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQI
-EDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8Q
-IECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAg
-QCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMs
-LkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBA
-gAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDA
-IPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAA
-AQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACB
-WMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA
-8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAEC
-BAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPM
-DxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQI
-ECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIB
-gywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQT
-IECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAg
-QMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9A
-gAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECA
-AIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyy
-uADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AA
-AQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAAB
-g8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAEC
-BAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIE
-YgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOIC
-xBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQI
-ECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwy
-P0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAg
-QIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgF
-DLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBP
-gAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECA
-AAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwA
-AQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAAB
-AgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI
-4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAEC
-BAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIE
-DDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQI
-ECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQ
-iAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsL
-EE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAg
-QIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI
-/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECA
-AAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAW
-MMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8
-AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAAB
-AgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMD
-BAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIE
-CBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAg
-iwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQI
-ECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQ
-MMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAg
-QIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBA
-IBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywu
-QDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECA
-AAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg
-8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAAB
-AgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFY
-wCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADx
-BAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIE
-CBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wP
-ECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQ
-IEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGD
-LC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMg
-QIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBA
-wCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CA
-AAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAA
-gVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4
-APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAAB
-AgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGD
-zA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIE
-CBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRi
-AYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLE
-EyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQ
-IEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/
-QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBA
-gACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUM
-srgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+A
-AAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAA
-AYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AAB
-AgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAEC
-BGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMji
-AsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIE
-CBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQM
-Mj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQ
-IECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCI
-BQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQ
-T4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBA
-gAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8
-AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAA
-AQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYw
-yOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwB
-AgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAEC
-BAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wME
-CBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQI
-EIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCL
-CxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQ
-IECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAw
-yPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBA
-gAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAg
-FjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5A
-PAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAA
-AQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDz
-AwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAEC
-BAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjA
-IIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEE
-CBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQI
-EDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8Q
-IECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAg
-QCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMs
-LkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBA
-gAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDA
-IPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAA
-AQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACB
-WMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA
-8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAEC
-BAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPM
-DxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQI
-ECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIB
-gywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQT
-IECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAg
-QMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9A
-gAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECA
-AIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyy
-uADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AA
-AQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAAB
-g8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAEC
-BAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIE
-YgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOIC
-xBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQI
-ECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwy
-P0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAg
-QIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgF
-DLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBP
-gAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECA
-AAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwA
-AQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAAB
-AgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI
-4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAEC
-BAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIE
-DDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQI
-ECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQ
-iAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsL
-EE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAg
-QIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI
-/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECA
-AAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAW
-MMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8
-AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAAB
-AgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMD
-BAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIE
-CBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAg
-iwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQI
-ECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQ
-MMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAABAgQIEIgFDLK4APEECBAgQIAAAYPMDxAg
-QIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFYwCCLCxBPgAABAgQIEDDI/AABAgQIECBA
-IBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADxBAgQIECAAAGDzA8QIECAAAECBGIBgywu
-QDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIECBAwyPwAAQIECBAgQCAWMMjiAsQTIECA
-AAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wPECBAgAABAgRiAYMsLkA8AQIECBAgQMAg
-8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQIEAgFjDI4gLEEyBAgAABAgQMMj9AgAAB
-AgQIEIgFDLK4APEECBAgQIAAAYPMDxAgQIAAAQIEYgGDLC5APAECBAgQIEDAIPMDBAgQIECAAIFY
-wCCLCxBPgAABAgQIEDDI/AABAgQIECBAIBYwyOICxBMgQIAAAQIEDDI/QIAAAQIECBCIBQyyuADx
-BAgQIECAAAGDzA8QIECAAAECBGIBgywuQDwBAgQIECBAwCDzAwQIECBAgACBWMAgiwsQT4AAAQIE
-CBAwyPwAAQIECBAgQCAWMMjiAsQTIECAAAECBAwyP0CAAAECBAgQiAUMsrgA8QQIECBAgAABg8wP
-ECBAgAABAgRiAYMsLkA8AQIECBAgQMAg8wMECBAgQIAAgVjAIIsLEE+AAAECBAgQMMj8AAECBAgQ
-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
-b7w9fTfueZOd73/e+no85cQ4n3a+6vxw83zPK8yV7fpH5xHPfP71T7///e9/8IMffLyR9vS5j5BP
-b/zDH/7wz3me+eaP9+SennzFOH+ueM9QPP3M/8l3H1+8dZ9BgAABAgT+JwXOOyX+vLDAxz/+8Ve8
-4hV//vOfn37aq171qp/85CfnI+fj5++//e1vz9/PKvrqV796fdr54PnPM27O1/7pT386fz+fc/5+
-febXvva1Z/7++Krr216f/NGPfvSZbOeD58/1wfNNTrbHJ5//en38RHo89Ap/BTh/vz7/+vv1oGdO
-uJ57Hfs457rxvCyfCfP0m5/vdgU7u+rxlEfC88FH7Mejrw9e2R5f/swj/FcCBAgQILAg4B2yF1+/
-3/72t7/yla+88Y1vfPp+0nnb7AMf+MD54te97nVf+tKXfv7zn5+3gn7wgx987GMfe/pe2je/+c3z
-487Xv/7154PnZ4gf+chHzmden/DlL3/5+qni+973vvPrWefLz9tUj297/tF56POG+/Wvf319/Hzh
-eaPregvqfIdPf/rTz/v5jwCf+tSnzuf/8+ecEfahD33oCnOF/M1vfvP0nHPjj370oxf+5u94xzvO
-J5yNdX5gesSuT37zm998PfGsrg9/+MPXB9/73vde77cdioNw4VyY3iR7XmQfJECAAIGXvYBB9pIq
-Plvn/FbWWTbvfOc7z2g48+IMoMdP3z73uc+d8f7Xv/71n38H/0y0pw84W+R85jOPfO1rX3u9e3R9
-25cU6F8/6Xzh+c9//ob/1nc4P3h93u9/5tEPf/jDxznXhHrec14g2xltj3/6+Onk+ano9cSzus4j
-rk/4whe+cIba+csvfvGLz3/+84/nvvTDfSYBAgQIEHiZCRhk/45Cr3dxzmy63tS5fs3/+vOZz3zm
-Na95zT9/r8fbQtc/+uUvf/kCz7u+7Uv/czbTS//kF/jMN73pTSfn03POpc97zgt8k8fvip1fC/vs
-Zz97fbczYa8vOT/0fEzY827cJfme97znvEP29Lkv/C8i/Jcc65sQIECAAIGggEH2IqWcefH4nf3z
-I8Xz2W9/+9uv97Se+Z+NuN4i+u53v3t9x/OL8+c/z1ti512i6yPny7/3ve+9//3v/7ce+fTbnk8+
-b7w972eed+muj3/xi188Q+rfO+Me3/Pxe/rnnbPz9tUzPy58es4ZW2dFPW+Yc901xa7Dz6I63/Yx
-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
+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
diff --git a/corpus/printout/cold/read/payloads.json b/corpus/printout/cold/read/payloads.json
index 1973a2e9f9b9a482fd4b86897627e4103866cb04..c967dc0801ed642802fdd2dd7352e507d25f30d5 100644
--- a/corpus/printout/cold/read/payloads.json
+++ b/corpus/printout/cold/read/payloads.json
@@ -2,9 +2,9 @@
{
"sha256": "e15f2722a8d11f9e68ac5eff54320f7c4ef5540fb6eb858223e31b983fa1bed7",
"name": "two-pages.docx",
- "object": "{DB7C4F71-B073-0121-2D07-84DA732F3A82}{23}{B0}",
+ "object": "{5B8B92BB-9FEB-06D8-085F-BB1AEF8C3FD6}{23}{B0}",
"kind": "InsertedFile",
- "page": "{DC91B969-8F8E-0E38-22C6-17772EFD8803}{1}{B0}",
+ "page": "{5C6664A3-A016-09C1-079E-28B7B25E8D57}{1}{B0}",
"bytes": 12589
}
]
\ No newline at end of file
diff --git a/corpus/printout/cold/run.json b/corpus/printout/cold/run.json
index 569608b03b8438b13f9f79044d5a6ea8191630d5..c95beb285e86d3609dfe66bbe82f5522e0c7f55e 100644
--- a/corpus/printout/cold/run.json
+++ b/corpus/printout/cold/run.json
@@ -1,5 +1,5 @@
{
- "notebook": "/private/tmp/unsup-lab/printout-candidate",
+ "notebook": "/private/tmp/printlab/candidate",
"expected_pages": 2,
"author": null,
"author_timeout_seconds": 600,
diff --git a/corpus/printout/cold/source.json b/corpus/printout/cold/source.json
index 62181874431e571489e154622f1c1b3ac870956a..1d663d3c9680c7878cbdf7c6d5a7cc295ba309e0 100644
--- a/corpus/printout/cold/source.json
+++ b/corpus/printout/cold/source.json
@@ -2,13 +2,13 @@
{
"path": "Open Notebook.onetoc2",
"bytes": 1612,
- "sha256": "29bbfd869154aea1720f98cf7c7cc40387313b00c8dfdb61d159f968312367b4",
- "mtime_ns": 1790744275764608328
+ "sha256": "fe8a117e418bb9aea7565bd92f01e03f87d763ba60bb5df24ab85b428dbd4f08",
+ "mtime_ns": 1790820802979688144
},
{
"path": "Printouts.one",
- "bytes": 268208,
- "sha256": "5b48da81b1b374da9af23b4dd664b01b100c34be71aef721907f4365afe5577e",
- "mtime_ns": 1790744275764329165
+ "bytes": 443488,
+ "sha256": "2be9306af1295605fd3deab3dfd16de2bfdeeecd6b696b48b4a6104b8d997c06",
+ "mtime_ns": 1790820802979090020
}
]
diff --git a/corpus/recycle-purge/README.md b/corpus/recycle-purge/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f4178b3bde82eab42cbcbf3d87145ef5d05cb67d
--- /dev/null
+++ b/corpus/recycle-purge/README.md
@@ -0,0 +1,19 @@
+# Recycle bin purge
+
+OneNote 2010 deletes for good what its Notebook Recycle Bin has held "after 60
+days" (`DaysToKeepRecycledItems`), a few minutes after it starts. Observed in a lab
+clone on 2026-09-30, with the clock moved to age pages and sections:
+
+- it judges by when the content last changed, not when it was deleted: pages
+ created 70 days back and deleted a minute before were purged, a page deleted
+ that day but changed that day was kept;
+- a page goes from `OneNote_DeletedPages.one` in one appended revision;
+- a binned section goes as a file once none of its pages changed in 60 days, its
+ file's own times aside; the bin's TOC keeps listing it, unchanged.
+
+`candidate/` is the notebook
+`the_recycle_bin_forgets_pages_and_sections_unchanged_for_sixty_days` in
+`crates/notebook/tests/structure.rs` exports with `NOTEBOOK_PURGE_EXPORT`: a page
+and a section deleted into the bin, then purged as 61 days on. `cold/` is a fresh
+OneNote 2010's read: Deleted Pages empty, the section gone, no repair, every file
+left as written. `tools/test_recycle_purge.py` checks it without a VM.
diff --git a/corpus/recycle-purge/candidate/New Section 1.one b/corpus/recycle-purge/candidate/New Section 1.one
new file mode 100644
index 0000000000000000000000000000000000000000..e9b0df4a741c3602037c7b329a32cab52d2a2f48
Binary files /dev/null and b/corpus/recycle-purge/candidate/New Section 1.one differ
diff --git a/corpus/recycle-purge/candidate/OneNote_RecycleBin/OneNote_DeletedPages.one b/corpus/recycle-purge/candidate/OneNote_RecycleBin/OneNote_DeletedPages.one
new file mode 100644
index 0000000000000000000000000000000000000000..04cd731a750a890d7f5d7552971ac0313dc161e3
Binary files /dev/null and b/corpus/recycle-purge/candidate/OneNote_RecycleBin/OneNote_DeletedPages.one differ
diff --git a/corpus/recycle-purge/candidate/OneNote_RecycleBin/Open Notebook.onetoc2 b/corpus/recycle-purge/candidate/OneNote_RecycleBin/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..8b1ba4c1fdab49f8f379fbce769fa8617a658c90
Binary files /dev/null and b/corpus/recycle-purge/candidate/OneNote_RecycleBin/Open Notebook.onetoc2 differ
diff --git a/corpus/recycle-purge/candidate/Open Notebook.onetoc2 b/corpus/recycle-purge/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..124f7a366627fd46ffdbb9b63237df1be7b1f9a0
Binary files /dev/null and b/corpus/recycle-purge/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/recycle-purge/cold/hierarchy.xml b/corpus/recycle-purge/cold/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f10a245d20c7fd79d99532d6b261e468979ac558
--- /dev/null
+++ b/corpus/recycle-purge/cold/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/recycle-purge/cold/recycle-bin.png b/corpus/recycle-purge/cold/recycle-bin.png
new file mode 100644
index 0000000000000000000000000000000000000000..f4f78ea1863b04f396f6fead703bf22eb8143ce1
Binary files /dev/null and b/corpus/recycle-purge/cold/recycle-bin.png differ
diff --git a/corpus/section-color/README.md b/corpus/section-color/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3b40b39d998ba1d55610f8eb8cf2e1ebf52de9b1
--- /dev/null
+++ b/corpus/section-color/README.md
@@ -0,0 +1,19 @@
+# Section and notebook colour
+
+`native` is what OneNote 2010 did on the VM (2026-09-30). A section tab's context menu
+ends with Section Color, sixteen named colours and None (`section-color-menu.png`);
+`colors.txt` is each as the hierarchy XML reports it, in the menu's order. Picking one
+appends a revision to the section's own file setting its metadata's `0x14001cbe`
+(COLORREF; `teal.one`), and None sets `0xFFFFFFFF` (`none.one`); the table of contents is
+left byte for byte. Notebook Properties (`notebook-properties.png`, from the notebook's
+Rename… or Properties…) offers the same sixteen without None (`notebook-colors.png`) and
+appends a revision to the root `Open Notebook.onetoc2` setting its root's `0x14001cbe`
+(`before.onetoc2`, `after.onetoc2`). Its Display name is written to neither file: OneNote
+keeps it in its local `OneNoteOfflineCache.onecache`, so the hierarchy's `nickname`
+changes and `name`, the folder, stays.
+
+`candidate` is `crates/snowbound/src/menus.rs`'s notebook, exported with
+`SNOWBOUND_SECTION_COLOR_EXPORT`: a section per colour and None, each made then coloured
+with `Notebook::set_section_color`, and the notebook coloured Teal with
+`Notebook::set_color`. `cold` is a fresh OneNote 2010's read: every section and the
+notebook in the colour it was given. `tools/test_section_color.py` checks it without a VM.
diff --git a/corpus/section-color/candidate/Apple.one b/corpus/section-color/candidate/Apple.one
new file mode 100644
index 0000000000000000000000000000000000000000..b787c8d09b1810e8797c84d01e19bbc197f5fb9f
Binary files /dev/null and b/corpus/section-color/candidate/Apple.one differ
diff --git a/corpus/section-color/candidate/Blue Mist.one b/corpus/section-color/candidate/Blue Mist.one
new file mode 100644
index 0000000000000000000000000000000000000000..5ab1b3f6e79ea98db8d9fb42f782bb93484fb455
Binary files /dev/null and b/corpus/section-color/candidate/Blue Mist.one differ
diff --git a/corpus/section-color/candidate/Blue.one b/corpus/section-color/candidate/Blue.one
new file mode 100644
index 0000000000000000000000000000000000000000..bee962892538a1cc8b21ee6c565f012f35160ad0
Binary files /dev/null and b/corpus/section-color/candidate/Blue.one differ
diff --git a/corpus/section-color/candidate/Cyan.one b/corpus/section-color/candidate/Cyan.one
new file mode 100644
index 0000000000000000000000000000000000000000..82ba5cb5cf2c0aad11ab6f667f8e6e69939499b8
Binary files /dev/null and b/corpus/section-color/candidate/Cyan.one differ
diff --git a/corpus/section-color/candidate/Green.one b/corpus/section-color/candidate/Green.one
new file mode 100644
index 0000000000000000000000000000000000000000..d7277aaf103ee2affd7778697cf16f890c136e6a
Binary files /dev/null and b/corpus/section-color/candidate/Green.one differ
diff --git a/corpus/section-color/candidate/Lemon.one b/corpus/section-color/candidate/Lemon.one
new file mode 100644
index 0000000000000000000000000000000000000000..354ad0e82fd4939b6c75a4b2c14bd3510a0d969e
Binary files /dev/null and b/corpus/section-color/candidate/Lemon.one differ
diff --git a/corpus/section-color/candidate/Magenta.one b/corpus/section-color/candidate/Magenta.one
new file mode 100644
index 0000000000000000000000000000000000000000..3e33155208bb8e977a5c78792ae78b76d2513cab
Binary files /dev/null and b/corpus/section-color/candidate/Magenta.one differ
diff --git a/corpus/section-color/candidate/New Section 1.one b/corpus/section-color/candidate/New Section 1.one
new file mode 100644
index 0000000000000000000000000000000000000000..ada9be84863be6afa0ec05b954e1f63001d62e71
Binary files /dev/null and b/corpus/section-color/candidate/New Section 1.one differ
diff --git a/corpus/section-color/candidate/None.one b/corpus/section-color/candidate/None.one
new file mode 100644
index 0000000000000000000000000000000000000000..f9e09b94ae65e0d5e345638e8fd817ab721515e5
Binary files /dev/null and b/corpus/section-color/candidate/None.one differ
diff --git a/corpus/section-color/candidate/Open Notebook.onetoc2 b/corpus/section-color/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..908f29936d3ac29bc3b3cb14cfbab85d3b1ba76f
Binary files /dev/null and b/corpus/section-color/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/section-color/candidate/Orange.one b/corpus/section-color/candidate/Orange.one
new file mode 100644
index 0000000000000000000000000000000000000000..711817ec4b3ab9edff5065e76291e78efadb130b
Binary files /dev/null and b/corpus/section-color/candidate/Orange.one differ
diff --git a/corpus/section-color/candidate/Purple Mist.one b/corpus/section-color/candidate/Purple Mist.one
new file mode 100644
index 0000000000000000000000000000000000000000..474bd09b5161f06701ac4780d79b98afe79393d5
Binary files /dev/null and b/corpus/section-color/candidate/Purple Mist.one differ
diff --git a/corpus/section-color/candidate/Purple.one b/corpus/section-color/candidate/Purple.one
new file mode 100644
index 0000000000000000000000000000000000000000..8f1c23884c40e475ae65ee84ec99b689dd861291
Binary files /dev/null and b/corpus/section-color/candidate/Purple.one differ
diff --git a/corpus/section-color/candidate/Red Chalk.one b/corpus/section-color/candidate/Red Chalk.one
new file mode 100644
index 0000000000000000000000000000000000000000..eec06b1e124581a4cbc27c06b30a74559c19d1a1
Binary files /dev/null and b/corpus/section-color/candidate/Red Chalk.one differ
diff --git a/corpus/section-color/candidate/Red.one b/corpus/section-color/candidate/Red.one
new file mode 100644
index 0000000000000000000000000000000000000000..655cad74b1d95d1744b9e69f2180789c2c751348
Binary files /dev/null and b/corpus/section-color/candidate/Red.one differ
diff --git a/corpus/section-color/candidate/Silver.one b/corpus/section-color/candidate/Silver.one
new file mode 100644
index 0000000000000000000000000000000000000000..daba64a266d8837c8f8a941ccc4c5157cb719ecb
Binary files /dev/null and b/corpus/section-color/candidate/Silver.one differ
diff --git a/corpus/section-color/candidate/Tan.one b/corpus/section-color/candidate/Tan.one
new file mode 100644
index 0000000000000000000000000000000000000000..04f5afecd23716f32893fc6ba74ceb81f1f0d45a
Binary files /dev/null and b/corpus/section-color/candidate/Tan.one differ
diff --git a/corpus/section-color/candidate/Teal.one b/corpus/section-color/candidate/Teal.one
new file mode 100644
index 0000000000000000000000000000000000000000..9d56e7288feb72ce27eafb8063ede4925ff5f318
Binary files /dev/null and b/corpus/section-color/candidate/Teal.one differ
diff --git a/corpus/section-color/candidate/Yellow.one b/corpus/section-color/candidate/Yellow.one
new file mode 100644
index 0000000000000000000000000000000000000000..1b8c50819b1f7a70821678d19e75452986b1ce0a
Binary files /dev/null and b/corpus/section-color/candidate/Yellow.one differ
diff --git a/corpus/section-color/cold/commands.jsonl b/corpus/section-color/cold/commands.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..7fa3aa60141e8fc7b2503e45bb57b5262d65ead3
--- /dev/null
+++ b/corpus/section-color/cold/commands.jsonl
@@ -0,0 +1,3 @@
+{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-35D6278F -ExpectedPages -1 -KeepOpen", "exit": 0, "stdout": "Read 18 sections and 18 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\read -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/section-color/cold/machine.json b/corpus/section-color/cold/machine.json
new file mode 100644
index 0000000000000000000000000000000000000000..66a80ed662b18a5858c91b75a9c3b502ba9a2b5e
--- /dev/null
+++ b/corpus/section-color/cold/machine.json
@@ -0,0 +1 @@
+{"name": "m6-35d6278f", "hostname": "ONE-M6-35D6278F"}
diff --git a/corpus/section-color/cold/read/environment.json b/corpus/section-color/cold/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..e5367d56aa664d3521b8e3f2fdca1a4da115b23f
--- /dev/null
+++ b/corpus/section-color/cold/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-35D6278F",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/section-color/cold/read/hierarchy.xml b/corpus/section-color/cold/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..16fd1d9270eca4e1a3abf8d251f9703e2a5db082
--- /dev/null
+++ b/corpus/section-color/cold/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-000.navigation.json b/corpus/section-color/cold/read/page-000.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-000.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-000.png b/corpus/section-color/cold/read/page-000.png
new file mode 100644
index 0000000000000000000000000000000000000000..d598c1229a73e117937784096e7b57cf6a0181e0
Binary files /dev/null and b/corpus/section-color/cold/read/page-000.png differ
diff --git a/corpus/section-color/cold/read/page-000.xml b/corpus/section-color/cold/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..01ed24d819243493efe518e4e034cbdefee660a9
--- /dev/null
+++ b/corpus/section-color/cold/read/page-000.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-001.navigation.json b/corpus/section-color/cold/read/page-001.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-001.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-001.png b/corpus/section-color/cold/read/page-001.png
new file mode 100644
index 0000000000000000000000000000000000000000..d53ecf3d1bbe76c43d3781cf684f8cd4b8ef112c
Binary files /dev/null and b/corpus/section-color/cold/read/page-001.png differ
diff --git a/corpus/section-color/cold/read/page-001.xml b/corpus/section-color/cold/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c2f09e78cd2ebbd3fdd6cd04b92c99fa76df015a
--- /dev/null
+++ b/corpus/section-color/cold/read/page-001.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-002.navigation.json b/corpus/section-color/cold/read/page-002.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-002.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-002.png b/corpus/section-color/cold/read/page-002.png
new file mode 100644
index 0000000000000000000000000000000000000000..4fae21273b9218fd87a393ec75a54c769658a2c2
Binary files /dev/null and b/corpus/section-color/cold/read/page-002.png differ
diff --git a/corpus/section-color/cold/read/page-002.xml b/corpus/section-color/cold/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7fe280535e3076187c6be7abf60f6553b57bbad7
--- /dev/null
+++ b/corpus/section-color/cold/read/page-002.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-003.navigation.json b/corpus/section-color/cold/read/page-003.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-003.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-003.png b/corpus/section-color/cold/read/page-003.png
new file mode 100644
index 0000000000000000000000000000000000000000..7296223cf8e0e9a194ec7e1c22302973a422d36b
Binary files /dev/null and b/corpus/section-color/cold/read/page-003.png differ
diff --git a/corpus/section-color/cold/read/page-003.xml b/corpus/section-color/cold/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9e63857829da737673ca92a9d2ca461ceb20aaad
--- /dev/null
+++ b/corpus/section-color/cold/read/page-003.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-004.navigation.json b/corpus/section-color/cold/read/page-004.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-004.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-004.png b/corpus/section-color/cold/read/page-004.png
new file mode 100644
index 0000000000000000000000000000000000000000..0dd5a0eb7eabde1c19a4a485c77c02bc33761ac1
Binary files /dev/null and b/corpus/section-color/cold/read/page-004.png differ
diff --git a/corpus/section-color/cold/read/page-004.xml b/corpus/section-color/cold/read/page-004.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8470dd40a686c1825cb0bddeb7d0b5eb6b99179b
--- /dev/null
+++ b/corpus/section-color/cold/read/page-004.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-005.navigation.json b/corpus/section-color/cold/read/page-005.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-005.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-005.png b/corpus/section-color/cold/read/page-005.png
new file mode 100644
index 0000000000000000000000000000000000000000..2555dec4248e7d4e33c038b774c94ef90d918d36
Binary files /dev/null and b/corpus/section-color/cold/read/page-005.png differ
diff --git a/corpus/section-color/cold/read/page-005.xml b/corpus/section-color/cold/read/page-005.xml
new file mode 100644
index 0000000000000000000000000000000000000000..832c4888d5751a64400f6e81f5a71c4241cd62f5
--- /dev/null
+++ b/corpus/section-color/cold/read/page-005.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-006.navigation.json b/corpus/section-color/cold/read/page-006.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-006.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-006.png b/corpus/section-color/cold/read/page-006.png
new file mode 100644
index 0000000000000000000000000000000000000000..5d5df88378f69bb329b562e440e6f37ec72dd4c1
Binary files /dev/null and b/corpus/section-color/cold/read/page-006.png differ
diff --git a/corpus/section-color/cold/read/page-006.xml b/corpus/section-color/cold/read/page-006.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1b9e64e0bfb53e602fd89f5488c9999cb228b6c5
--- /dev/null
+++ b/corpus/section-color/cold/read/page-006.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-007.navigation.json b/corpus/section-color/cold/read/page-007.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-007.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-007.png b/corpus/section-color/cold/read/page-007.png
new file mode 100644
index 0000000000000000000000000000000000000000..d8607670eb678812135d7a98162366b3d16d8fdf
Binary files /dev/null and b/corpus/section-color/cold/read/page-007.png differ
diff --git a/corpus/section-color/cold/read/page-007.xml b/corpus/section-color/cold/read/page-007.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3a177c868a0f72301963f1895b5977bcbf1994fc
--- /dev/null
+++ b/corpus/section-color/cold/read/page-007.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-008.navigation.json b/corpus/section-color/cold/read/page-008.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-008.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-008.png b/corpus/section-color/cold/read/page-008.png
new file mode 100644
index 0000000000000000000000000000000000000000..7288dc7dedc537fa0f82121a815b5cb52e909be3
Binary files /dev/null and b/corpus/section-color/cold/read/page-008.png differ
diff --git a/corpus/section-color/cold/read/page-008.xml b/corpus/section-color/cold/read/page-008.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f9fb951e4b35cdaa3e614810c9b3d0ee29baeb9a
--- /dev/null
+++ b/corpus/section-color/cold/read/page-008.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-009.navigation.json b/corpus/section-color/cold/read/page-009.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-009.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-009.png b/corpus/section-color/cold/read/page-009.png
new file mode 100644
index 0000000000000000000000000000000000000000..83970fb350ed83698254f8b42297c5b38a69a927
Binary files /dev/null and b/corpus/section-color/cold/read/page-009.png differ
diff --git a/corpus/section-color/cold/read/page-009.xml b/corpus/section-color/cold/read/page-009.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6403eaf72fcb86d54010f519e3bbfdd3ca390585
--- /dev/null
+++ b/corpus/section-color/cold/read/page-009.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-010.navigation.json b/corpus/section-color/cold/read/page-010.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-010.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-010.png b/corpus/section-color/cold/read/page-010.png
new file mode 100644
index 0000000000000000000000000000000000000000..eeb4a5b79c5f6f7bc024483ae59c81b0c05d72b7
Binary files /dev/null and b/corpus/section-color/cold/read/page-010.png differ
diff --git a/corpus/section-color/cold/read/page-010.xml b/corpus/section-color/cold/read/page-010.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a8f908dadc6435e2c5d64924869d2f6926075476
--- /dev/null
+++ b/corpus/section-color/cold/read/page-010.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-011.navigation.json b/corpus/section-color/cold/read/page-011.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-011.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-011.png b/corpus/section-color/cold/read/page-011.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3b39eef8e2cb6d8149cbd9838058aac16a2aba5
Binary files /dev/null and b/corpus/section-color/cold/read/page-011.png differ
diff --git a/corpus/section-color/cold/read/page-011.xml b/corpus/section-color/cold/read/page-011.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5f95a1d4e8b24798c019494f8fcaf3e80f9e1d3d
--- /dev/null
+++ b/corpus/section-color/cold/read/page-011.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-012.navigation.json b/corpus/section-color/cold/read/page-012.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-012.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-012.png b/corpus/section-color/cold/read/page-012.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc18ba287f557d4bb21b8daf79cf66ece3acf1c5
Binary files /dev/null and b/corpus/section-color/cold/read/page-012.png differ
diff --git a/corpus/section-color/cold/read/page-012.xml b/corpus/section-color/cold/read/page-012.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8bac391fbc5ac57c8809c0bd985d9cbde40110a7
--- /dev/null
+++ b/corpus/section-color/cold/read/page-012.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-013.navigation.json b/corpus/section-color/cold/read/page-013.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-013.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-013.png b/corpus/section-color/cold/read/page-013.png
new file mode 100644
index 0000000000000000000000000000000000000000..1155faa5cca9d0f0748f0d1defa09a8e1840a273
Binary files /dev/null and b/corpus/section-color/cold/read/page-013.png differ
diff --git a/corpus/section-color/cold/read/page-013.xml b/corpus/section-color/cold/read/page-013.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7acef30255e2d0f76dce549a0d3b6f468203cc6c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-013.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-014.navigation.json b/corpus/section-color/cold/read/page-014.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-014.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-014.png b/corpus/section-color/cold/read/page-014.png
new file mode 100644
index 0000000000000000000000000000000000000000..e066e7ad700cb207ff18ebef939f3b74260f0c1a
Binary files /dev/null and b/corpus/section-color/cold/read/page-014.png differ
diff --git a/corpus/section-color/cold/read/page-014.xml b/corpus/section-color/cold/read/page-014.xml
new file mode 100644
index 0000000000000000000000000000000000000000..315d24c03b00837cb4e54139cbd2f88e459baa03
--- /dev/null
+++ b/corpus/section-color/cold/read/page-014.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-015.navigation.json b/corpus/section-color/cold/read/page-015.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-015.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-015.png b/corpus/section-color/cold/read/page-015.png
new file mode 100644
index 0000000000000000000000000000000000000000..f59398bd4b046cd1ec9723d25f76ddcc7ac6bcab
Binary files /dev/null and b/corpus/section-color/cold/read/page-015.png differ
diff --git a/corpus/section-color/cold/read/page-015.xml b/corpus/section-color/cold/read/page-015.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d2086ce60fd9a78555437e798ce2a7dd90b6d048
--- /dev/null
+++ b/corpus/section-color/cold/read/page-015.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-016.navigation.json b/corpus/section-color/cold/read/page-016.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-016.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-016.png b/corpus/section-color/cold/read/page-016.png
new file mode 100644
index 0000000000000000000000000000000000000000..82652cd55121fe83becd78fde0d36e084f3bf18a
Binary files /dev/null and b/corpus/section-color/cold/read/page-016.png differ
diff --git a/corpus/section-color/cold/read/page-016.xml b/corpus/section-color/cold/read/page-016.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b3526a71557bc830670e59554e9362dbb72c1ee9
--- /dev/null
+++ b/corpus/section-color/cold/read/page-016.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/page-017.navigation.json b/corpus/section-color/cold/read/page-017.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a3ec3aa40d7375c559da1ef012a98cc97da4412c
--- /dev/null
+++ b/corpus/section-color/cold/read/page-017.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Untitled page - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/section-color/cold/read/page-017.png b/corpus/section-color/cold/read/page-017.png
new file mode 100644
index 0000000000000000000000000000000000000000..99f9356aa8b512b61a48299c20c847e24ffb0212
Binary files /dev/null and b/corpus/section-color/cold/read/page-017.png differ
diff --git a/corpus/section-color/cold/read/page-017.xml b/corpus/section-color/cold/read/page-017.xml
new file mode 100644
index 0000000000000000000000000000000000000000..891eaa1c3985f00f00f366187040885f0b72d891
--- /dev/null
+++ b/corpus/section-color/cold/read/page-017.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/section-color/cold/read/payloads.json b/corpus/section-color/cold/read/payloads.json
new file mode 100644
index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843
--- /dev/null
+++ b/corpus/section-color/cold/read/payloads.json
@@ -0,0 +1,3 @@
+[
+
+]
\ No newline at end of file
diff --git a/corpus/section-color/cold/run.json b/corpus/section-color/cold/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..658d74ac8f153327cac9802ca8d89f3ac78bbe6c
--- /dev/null
+++ b/corpus/section-color/cold/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/private/tmp/snowbound-properties/section-color/candidate",
+ "expected_pages": -1,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": false,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35"
+ }
+}
diff --git a/corpus/section-color/cold/scripts/cold.ps1 b/corpus/section-color/cold/scripts/cold.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f
--- /dev/null
+++ b/corpus/section-color/cold/scripts/cold.ps1
@@ -0,0 +1,27 @@
+param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '')
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' }
+$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote'
+if ($CloneHost) {
+ if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) {
+ throw 'The disposable clone hostname does not match this machine.'
+ }
+ New-Item "$key\Options\Paths" -Force | Out-Null
+ New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null
+} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or
+ -not (Test-Path 'C:\one-tests\profile-original-cache')) {
+ throw 'Park the personal OneNote profile before resetting the test cache.'
+}
+$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0'
+$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root))
+if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' }
+New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null
+if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked }
+if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse }
+New-Item "$key\OpenNotebooks" | Out-Null
+New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null
diff --git a/corpus/section-color/cold/scripts/read.ps1 b/corpus/section-color/cold/scripts/read.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc
--- /dev/null
+++ b/corpus/section-color/cold/scripts/read.ps1
@@ -0,0 +1,144 @@
+param(
+ [Parameter(Mandatory=$true)][string]$Root,
+ [int]$ExpectedPages = -1,
+ [switch]$UseCurrentCache,
+ [switch]$Pdf,
+ [switch]$KeepOpen,
+ [string]$CloneHost = ''
+)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+$notebook = Join-Path $root 'notebook'
+$output = Join-Path $root 'read'
+if (Test-Path $output) { throw 'Choose a new read destination.' }
+if ($UseCurrentCache) {
+ if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') {
+ throw 'Park the personal OneNote profile before reading test notebooks.'
+ }
+} else {
+ & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost
+}
+New-Item -ItemType Directory -Path $output | Out-Null
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+$failure = $null
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $process = Get-Process ONENOTE
+ @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion;
+ powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } |
+ ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8
+ $sections = @()
+ foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) {
+ $id = ''
+ $app.OpenHierarchy($file.FullName, '', [ref]$id, 0)
+ $sections += $id
+ }
+ $deadline = [DateTime]::UtcNow.AddSeconds(300)
+ $previous = ''
+ $lastChange = ''
+ $stableSince = [DateTime]::UtcNow
+ $settled = $false
+ do {
+ $pages = @{}
+ foreach ($section in $sections) {
+ $hierarchy = ''
+ $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
+ [xml]$xml = $hierarchy
+ foreach ($node in $xml.SelectNodes('//*[@path]')) {
+ if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'OneNote opened a section outside the copied notebook.'
+ }
+ }
+ foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) {
+ $id = $node.GetAttribute('ID')
+ $content = ''
+ $app.GetPageContent($id, [ref]$content, 1, 1)
+ $pages[$id] = $content
+ }
+ }
+ $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] }))
+ if ($signature -ne $previous) {
+ $lastChange = $previous
+ $previous = $signature
+ $stableSince = [DateTime]::UtcNow
+ }
+ if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or
+ ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and
+ ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break }
+ Start-Sleep -Milliseconds 250
+ } while ([DateTime]::UtcNow -lt $deadline)
+ if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) {
+ [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8)
+ $index = 0
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ $index++
+ }
+ $hierarchy = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1)
+ [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8)
+ throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled."
+ }
+ $index = 0
+ $payloads = @()
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ if ($Pdf) {
+ $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index)
+ $app.NavigateTo($id, '', $false)
+ $app.Publish($id, $pdfPath, 3, '')
+ if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' }
+ }
+ [xml]$page = $pages[$id]
+ foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) {
+ $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache'))
+ $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant()
+ [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes)
+ # A paragraph's file is named by its paragraph, one on the page by itself.
+ $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode }
+ $payloads += @{ page = $id; object = $owner.GetAttribute('objectID');
+ kind = $file.LocalName; name = $file.GetAttribute('preferredName');
+ sha256 = $hash; bytes = $bytes.Length }
+ }
+ $index++
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8)
+ $all = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$all, 1)
+ [xml]$finalTree = $all
+ $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique)
+ if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) {
+ throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.'
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8)
+ Write-Output "Read $($sections.Count) sections and $($pages.Count) pages."
+} catch {
+ $failure = $_
+ [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ throw
+} finally {
+ try {
+ try {
+ if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) }
+ if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) {
+ $app.CloseNotebook($notebookId, $false)
+ }
+ } catch {
+ if ($null -eq $failure) { throw }
+ [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ }
+ } finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ }
+ $app = $null
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+ if (-not $UseCurrentCache -and -not $CloneHost) {
+ Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10
+ }
+}
diff --git a/corpus/section-color/cold/source.json b/corpus/section-color/cold/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..6980c1b6282dd1456dbcb3dba4ab6d04bb27709c
--- /dev/null
+++ b/corpus/section-color/cold/source.json
@@ -0,0 +1,116 @@
+[
+ {
+ "path": "Apple.one",
+ "bytes": 6352,
+ "sha256": "0bf6021d0904aa6e281574ce50db4f24d5bb94980140b444e21b9720e9d4c0d4",
+ "mtime_ns": 1790825341918454713
+ },
+ {
+ "path": "Blue Mist.one",
+ "bytes": 6352,
+ "sha256": "d9f3430fc9d4671a22a62a5575513df6cb710376d709e6bff47fab2598a7cc15",
+ "mtime_ns": 1790825341692411886
+ },
+ {
+ "path": "Blue.one",
+ "bytes": 6352,
+ "sha256": "10811cc79043e79e26a908c4974a6f8b8df923223a509d2673adbe0b9b060f1d",
+ "mtime_ns": 1790825341319295896
+ },
+ {
+ "path": "Cyan.one",
+ "bytes": 6352,
+ "sha256": "d51a7b08f6cda049ea8209174498d50283c7ec9dc0a63583b35029aeb0c4e578",
+ "mtime_ns": 1790825341540263286
+ },
+ {
+ "path": "Green.one",
+ "bytes": 6352,
+ "sha256": "470c4d19a7ca9d03c1243604bb8d4142feb5ca33c1e9244c3732f0275b02315d",
+ "mtime_ns": 1790825341404235629
+ },
+ {
+ "path": "Lemon.one",
+ "bytes": 6352,
+ "sha256": "a30ee07c1d1eaabd7d8ac2dd5ba93a12d62db8ca5602f11ef9cdaf2127461d45",
+ "mtime_ns": 1790825341865471198
+ },
+ {
+ "path": "Magenta.one",
+ "bytes": 6352,
+ "sha256": "d2fb77176b7b891f822e9b4e0a86071c811bcef4655e066c66aaf101661eead1",
+ "mtime_ns": 1790825341641456588
+ },
+ {
+ "path": "New Section 1.one",
+ "bytes": 6000,
+ "sha256": "8f9614276e8f8a60f681660b2015c0f2f59428eedef503087c5aea8874d7fc7b",
+ "mtime_ns": 1790825341261553569
+ },
+ {
+ "path": "None.one",
+ "bytes": 6352,
+ "sha256": "d61d1899f38e371b042d40641bc7bd1ba6eadf78b205bb20bcd1a9470e5eff53",
+ "mtime_ns": 1790825342154418500
+ },
+ {
+ "path": "Open Notebook.onetoc2",
+ "bytes": 14552,
+ "sha256": "61b231025ae93c0242bd59468e14f493ccb24c231c59fcb2fae28ad4c6efc8a6",
+ "mtime_ns": 1790825342179576107
+ },
+ {
+ "path": "Orange.one",
+ "bytes": 6352,
+ "sha256": "1635f1fb9340b69e5abe82ef00bc99df332d83708187f44f573f5d887630c374",
+ "mtime_ns": 1790825341589404452
+ },
+ {
+ "path": "Purple Mist.one",
+ "bytes": 6352,
+ "sha256": "aa06592b69ced1ddd935d625eba1c4e02fd084e9c032ba5976226cf7a59cbc21",
+ "mtime_ns": 1790825341748511081
+ },
+ {
+ "path": "Purple.one",
+ "bytes": 6352,
+ "sha256": "4ade0b3953c99a4ab8e29a7855370c9a3bf1b9e06c359fd2f523abf167dc8797",
+ "mtime_ns": 1790825341493283421
+ },
+ {
+ "path": "Red Chalk.one",
+ "bytes": 6352,
+ "sha256": "1e9c778c50a963c66ab375e1e8f67cff05e2a11d95a5a89abf0dfa09dcc6d05a",
+ "mtime_ns": 1790825342043389615
+ },
+ {
+ "path": "Red.one",
+ "bytes": 6352,
+ "sha256": "f17f0e4de9947c3b40567f8ba60a9ec3f127c1bb7dfdc55b778e7b08e1f3b60d",
+ "mtime_ns": 1790825341447219727
+ },
+ {
+ "path": "Silver.one",
+ "bytes": 6352,
+ "sha256": "aa854f8b8c8333ae2d274d9a17333bb3a5c7c62bb35e69da1640385df3904101",
+ "mtime_ns": 1790825342101432402
+ },
+ {
+ "path": "Tan.one",
+ "bytes": 6352,
+ "sha256": "236d18dff5679e7bf10c108c44d18ab8df1d01cf860c2bd94b0d74fdb0c81015",
+ "mtime_ns": 1790825341806399157
+ },
+ {
+ "path": "Teal.one",
+ "bytes": 6352,
+ "sha256": "fbf08e85eee0b224bb76b82e3541fee9abe033bc04dd7a6abb09a8c0ae774e5f",
+ "mtime_ns": 1790825341979577971
+ },
+ {
+ "path": "Yellow.one",
+ "bytes": 6352,
+ "sha256": "a30a5231b8ec1a0d603c325694f538e432e6095d601dbcce002c2e663b42f3f7",
+ "mtime_ns": 1790825341361229906
+ }
+]
diff --git a/corpus/section-color/cold/teardown.json b/corpus/section-color/cold/teardown.json
new file mode 100644
index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70
--- /dev/null
+++ b/corpus/section-color/cold/teardown.json
@@ -0,0 +1 @@
+{"absent": true}
diff --git a/corpus/section-color/native/after.onetoc2 b/corpus/section-color/native/after.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..3050eeed527df03016d22a8b3f3a810cb97d56d9
Binary files /dev/null and b/corpus/section-color/native/after.onetoc2 differ
diff --git a/corpus/section-color/native/before.onetoc2 b/corpus/section-color/native/before.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..6597a3c91d618775244dae61595dea3fe5584ebd
Binary files /dev/null and b/corpus/section-color/native/before.onetoc2 differ
diff --git a/corpus/section-color/native/colors.txt b/corpus/section-color/native/colors.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6212bd9a9b3b387be0eda534f9218d5b3e50f9ef
--- /dev/null
+++ b/corpus/section-color/native/colors.txt
@@ -0,0 +1,17 @@
+Blue #8AA8E4
+Yellow #FFD869
+Green #B7C997
+Red #EE9597
+Purple #B49EDE
+Cyan #91BAAE
+Orange #F6B078
+Magenta #D5A4BB
+Blue Mist #9BBBD2
+Purple Mist #AB9CB7
+Tan #E8D199
+Lemon #F5F96F
+Apple #ADE792
+Teal #4DBCCA
+Red Chalk #BA7575
+Silver #9595AA
+None none
diff --git a/corpus/section-color/native/none.one b/corpus/section-color/native/none.one
new file mode 100644
index 0000000000000000000000000000000000000000..3b7d710737f3f3c8dc3cebe3f5ab5bd5c8f0adb1
Binary files /dev/null and b/corpus/section-color/native/none.one differ
diff --git a/corpus/section-color/native/notebook-colors.png b/corpus/section-color/native/notebook-colors.png
new file mode 100644
index 0000000000000000000000000000000000000000..7f3c1bb7f1ed4ea47860e854a3382432b9da24cd
Binary files /dev/null and b/corpus/section-color/native/notebook-colors.png differ
diff --git a/corpus/section-color/native/notebook-menu.png b/corpus/section-color/native/notebook-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..3badf10fef1f8dec4b9637f0865c434d10474c64
Binary files /dev/null and b/corpus/section-color/native/notebook-menu.png differ
diff --git a/corpus/section-color/native/notebook-properties.png b/corpus/section-color/native/notebook-properties.png
new file mode 100644
index 0000000000000000000000000000000000000000..33ad64b554afb3d03540eda2a85fb3127c98fcf0
Binary files /dev/null and b/corpus/section-color/native/notebook-properties.png differ
diff --git a/corpus/section-color/native/section-color-menu.png b/corpus/section-color/native/section-color-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..9b570b5a49ab3398dec7f273b1f2394b90743e47
Binary files /dev/null and b/corpus/section-color/native/section-color-menu.png differ
diff --git a/corpus/section-color/native/section-menu.png b/corpus/section-color/native/section-menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..19858bbcc6b1785bc015a6a1c0226ad42d03ce1c
Binary files /dev/null and b/corpus/section-color/native/section-menu.png differ
diff --git a/corpus/section-color/native/teal.one b/corpus/section-color/native/teal.one
new file mode 100644
index 0000000000000000000000000000000000000000..9f01c9f8eb2dc58d799ec4bc7d534d414358c905
Binary files /dev/null and b/corpus/section-color/native/teal.one differ
diff --git a/corpus/styles/README.md b/corpus/styles/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..2dbcc220d5ca00285a888ebf7ccf1e1e0083442d
--- /dev/null
+++ b/corpus/styles/README.md
@@ -0,0 +1,27 @@
+# Styles and themes
+
+`onenote/` is OneNote 2010 applying its own Styles gallery in the lab (2026-09-30):
+`Styles.one` holds a page with each of the eleven styles applied (Ctrl+Alt+1 to 6 and the
+gallery), Enter after each, a style over explicit formatting, Clear Formatting, and a second
+page whose styles were made through COM in Georgia, onto which OneNote added its own `h1`
+and into which a pasted Georgia heading brought its own; `applied.xml` is the first page's
+COM read. Applying a style stores a paragraph style object per style per page and clears
+the paragraph's explicit character formatting; headings carry NextStyle `p`.
+
+`candidate/` is the notebook `themed_pages_hold_their_theme_under_onenote_s_names` in
+`crates/snowbound/src/themes.rs` writes: a section per built-in theme (OneNote, Manuscript,
+Editorial, Modern), each a page with a paragraph in every gallery style applied through the
+editor, then restyled as opening the page does, and `.snowbound/themes.json` giving each
+section its theme. `cold/` is `tools/native_styles.py` in a fresh OneNote 2010 clone: every
+page draws in its theme (`read/page-00N.png`) and lists the eleven styles under OneNote's
+names with the theme's values, and no `.snowbound` section group appears.
+
+`onenote-edit/` is OneNote 2010 editing the Manuscript page of `candidate/` in the lab:
+Ctrl+Alt+2 on the Normal paragraph and Enter after it, and Enter after the theme's Heading
+5. OneNote applies its own `h2` and `p` beside the theme's (`Manuscript.png`); reopened in
+Snowbound they read under those names and restyling heals them
+(`styles_onenote_adds_to_a_themed_page_are_healed`).
+
+`tools/test_styles.py` checks this without a VM. Regenerate with `SNOWBOUND_STYLES_EXPORT`
+set to a new absolute directory while running the test, then `tools/native_styles.py OUTPUT
+COLD`.
diff --git a/corpus/styles/candidate/.snowbound/themes.json b/corpus/styles/candidate/.snowbound/themes.json
new file mode 100644
index 0000000000000000000000000000000000000000..549945dd36f23fa7a726c139f4d6865ceaedd612
--- /dev/null
+++ b/corpus/styles/candidate/.snowbound/themes.json
@@ -0,0 +1,33 @@
+{
+ "themes": [],
+ "assignments": [
+ {
+ "scope": {
+ "section": "125e8f01c6ee404b96cc675d00ceb659"
+ },
+ "theme": "onenote",
+ "assigned": 134000000000000000
+ },
+ {
+ "scope": {
+ "section": "af4f8ea23c5f7e41838ac7cbea473590"
+ },
+ "theme": "manuscript",
+ "assigned": 134000000000000000
+ },
+ {
+ "scope": {
+ "section": "18af2996b565454c971191d887028fc8"
+ },
+ "theme": "editorial",
+ "assigned": 134000000000000000
+ },
+ {
+ "scope": {
+ "section": "f0ca724b3a9d3d4b82bc6f28bd77966c"
+ },
+ "theme": "modern",
+ "assigned": 134000000000000000
+ }
+ ]
+}
\ No newline at end of file
diff --git a/corpus/styles/candidate/Editorial.one b/corpus/styles/candidate/Editorial.one
new file mode 100644
index 0000000000000000000000000000000000000000..1f5b13635c9519f0b1d6470a2a80781333fa1743
Binary files /dev/null and b/corpus/styles/candidate/Editorial.one differ
diff --git a/corpus/styles/candidate/Manuscript.one b/corpus/styles/candidate/Manuscript.one
new file mode 100644
index 0000000000000000000000000000000000000000..d595551c7d0126f3a0f5e118d6bf55b45c038576
Binary files /dev/null and b/corpus/styles/candidate/Manuscript.one differ
diff --git a/corpus/styles/candidate/Modern.one b/corpus/styles/candidate/Modern.one
new file mode 100644
index 0000000000000000000000000000000000000000..7228d5e88fbbda251c692ea98fe89da570505f84
Binary files /dev/null and b/corpus/styles/candidate/Modern.one differ
diff --git a/corpus/styles/candidate/OneNote.one b/corpus/styles/candidate/OneNote.one
new file mode 100644
index 0000000000000000000000000000000000000000..6915847a3b5e8c59004dcd085c9d9705da76d783
Binary files /dev/null and b/corpus/styles/candidate/OneNote.one differ
diff --git a/corpus/styles/candidate/Open Notebook.onetoc2 b/corpus/styles/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..d4ceb28b8e6899338a0ccf41cc757423a03dd3bd
Binary files /dev/null and b/corpus/styles/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/styles/cold/commands.jsonl b/corpus/styles/cold/commands.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..0843332e2b3ce3020bb737357ca84f70ba069ead
--- /dev/null
+++ b/corpus/styles/cold/commands.jsonl
@@ -0,0 +1,4 @@
+{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\author.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-222E5D78", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-222E5D78 -ExpectedPages 4 -UseCurrentCache -KeepOpen", "exit": 0, "stdout": "Read 4 sections and 4 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/styles/cold/machine.json b/corpus/styles/cold/machine.json
new file mode 100644
index 0000000000000000000000000000000000000000..5d1ccba7f887bfaf23686dfb5c2dae77c791d0be
--- /dev/null
+++ b/corpus/styles/cold/machine.json
@@ -0,0 +1 @@
+{"name": "m6-222e5d78", "hostname": "ONE-M6-222E5D78"}
diff --git a/corpus/styles/cold/notebook/Editorial.one b/corpus/styles/cold/notebook/Editorial.one
new file mode 100644
index 0000000000000000000000000000000000000000..42462a2e46e8b31d90bfd6e8fc0254a167a8add7
Binary files /dev/null and b/corpus/styles/cold/notebook/Editorial.one differ
diff --git a/corpus/styles/cold/notebook/Manuscript.one b/corpus/styles/cold/notebook/Manuscript.one
new file mode 100644
index 0000000000000000000000000000000000000000..8f3ce5fb57b2b2ff62ed6076782a98fac5baf849
Binary files /dev/null and b/corpus/styles/cold/notebook/Manuscript.one differ
diff --git a/corpus/styles/cold/notebook/Modern.one b/corpus/styles/cold/notebook/Modern.one
new file mode 100644
index 0000000000000000000000000000000000000000..403d594c04d6df0f50a8d65dc8c5b0178a5c40de
Binary files /dev/null and b/corpus/styles/cold/notebook/Modern.one differ
diff --git a/corpus/styles/cold/notebook/OneNote.one b/corpus/styles/cold/notebook/OneNote.one
new file mode 100644
index 0000000000000000000000000000000000000000..eabc7f8002f2b284c95f9b55622de02338c54e86
Binary files /dev/null and b/corpus/styles/cold/notebook/OneNote.one differ
diff --git a/corpus/styles/cold/notebook/Open Notebook.onetoc2 b/corpus/styles/cold/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..6a5357126ab396e70a19ac2ae665da6f24f35860
Binary files /dev/null and b/corpus/styles/cold/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/styles/cold/read/environment.json b/corpus/styles/cold/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..e93a5211ce6a1aba021b77c79ee65472d25d7ba3
--- /dev/null
+++ b/corpus/styles/cold/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-222E5D78",
+ "cold": false,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/styles/cold/read/hierarchy.xml b/corpus/styles/cold/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..21cad963c0a70f32b9f24f30a03e43419f1311f5
--- /dev/null
+++ b/corpus/styles/cold/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/styles/cold/read/page-000.navigation.json b/corpus/styles/cold/read/page-000.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..556fccc0ba9aafbacaa8b574185841ec2226b9ac
--- /dev/null
+++ b/corpus/styles/cold/read/page-000.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Heading 1 in Manuscript - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/styles/cold/read/page-000.png b/corpus/styles/cold/read/page-000.png
new file mode 100644
index 0000000000000000000000000000000000000000..cf74c1e244c403efb231e2bd111d7986ab78ec70
Binary files /dev/null and b/corpus/styles/cold/read/page-000.png differ
diff --git a/corpus/styles/cold/read/page-000.xml b/corpus/styles/cold/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0a3a9b4653d5d2510d1851ed2749ae3eecf056d9
--- /dev/null
+++ b/corpus/styles/cold/read/page-000.xml
@@ -0,0 +1,7 @@
+
+Heading 2 in Manuscript]]>Heading 3 in Manuscript]]>Heading 4 in Manuscript]]>Heading 6 in Manuscript]]>Quote in Manuscript]]>
diff --git a/corpus/styles/cold/read/page-001.navigation.json b/corpus/styles/cold/read/page-001.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..eb3009ecdd53b4c3f537147cba67b7f35fb93b77
--- /dev/null
+++ b/corpus/styles/cold/read/page-001.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Heading 1 in OneNote - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/styles/cold/read/page-001.png b/corpus/styles/cold/read/page-001.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5eb0b44677bb5f2dc2c2b6d278330c5775261da
Binary files /dev/null and b/corpus/styles/cold/read/page-001.png differ
diff --git a/corpus/styles/cold/read/page-001.xml b/corpus/styles/cold/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ebc52a01470deea7c86e2d90066b47c2c3ebbfec
--- /dev/null
+++ b/corpus/styles/cold/read/page-001.xml
@@ -0,0 +1,8 @@
+
+Heading 1 in OneNote]]>Heading 2 in OneNote]]>Heading 3 in OneNote]]>Heading 4 in OneNote]]>Heading 6 in OneNote]]>Quote in OneNote]]>
diff --git a/corpus/styles/cold/read/page-002.navigation.json b/corpus/styles/cold/read/page-002.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..5c4ad5710c39a47780832ac8e8af3262af484508
--- /dev/null
+++ b/corpus/styles/cold/read/page-002.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Heading 1 in Editorial - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/styles/cold/read/page-002.png b/corpus/styles/cold/read/page-002.png
new file mode 100644
index 0000000000000000000000000000000000000000..235a6a47216251b5bd1ef8b008a07c93d72345c0
Binary files /dev/null and b/corpus/styles/cold/read/page-002.png differ
diff --git a/corpus/styles/cold/read/page-002.xml b/corpus/styles/cold/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..58207e109ab7de0708bea486bea44f6486295835
--- /dev/null
+++ b/corpus/styles/cold/read/page-002.xml
@@ -0,0 +1,8 @@
+
+Heading 1 in Editorial]]>Heading 2 in Editorial]]>Heading 3 in Editorial]]>Heading 4 in Editorial]]>Heading 6 in Editorial]]>Quote in Editorial]]>
diff --git a/corpus/styles/cold/read/page-003.navigation.json b/corpus/styles/cold/read/page-003.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..fbef07913da8ee5d5dac2283fcacb7e449ba65d5
--- /dev/null
+++ b/corpus/styles/cold/read/page-003.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Heading 1 in Modern - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/styles/cold/read/page-003.png b/corpus/styles/cold/read/page-003.png
new file mode 100644
index 0000000000000000000000000000000000000000..6cab06bc59975eb692f514c892f4cc47c1807d3c
Binary files /dev/null and b/corpus/styles/cold/read/page-003.png differ
diff --git a/corpus/styles/cold/read/page-003.xml b/corpus/styles/cold/read/page-003.xml
new file mode 100644
index 0000000000000000000000000000000000000000..60f71dd65b7be922721730dfa3f9c45bc3e4a52a
--- /dev/null
+++ b/corpus/styles/cold/read/page-003.xml
@@ -0,0 +1,10 @@
+
+Heading 1 in Modern]]>Heading 2 in Modern]]>Heading 3 in Modern]]>Heading 4 in Modern]]>Heading 5 in Modern]]>Heading 6 in Modern]]>Page Title in Modern]]>Quote in Modern]]>
diff --git a/corpus/styles/cold/read/payloads.json b/corpus/styles/cold/read/payloads.json
new file mode 100644
index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843
--- /dev/null
+++ b/corpus/styles/cold/read/payloads.json
@@ -0,0 +1,3 @@
+[
+
+]
\ No newline at end of file
diff --git a/corpus/styles/cold/run.json b/corpus/styles/cold/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..8c605f3ac1747131b7482d6df45fe748c6729c44
--- /dev/null
+++ b/corpus/styles/cold/run.json
@@ -0,0 +1,19 @@
+{
+ "notebook": "/private/tmp/snowbound-styles/candidate",
+ "expected_pages": 4,
+ "author": "/Users/clo/dev/one/tools/native/styles.ps1",
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "author.ps1": "fee6de8f8db4c294d9b39f5cf6428fa6031b52b9158470a3f0aeaf82ef64bfa8",
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35"
+ }
+}
diff --git a/corpus/styles/cold/scripts/author.ps1 b/corpus/styles/cold/scripts/author.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..efba77f99a1621c369f3cc3637b8845fd4f225ea
--- /dev/null
+++ b/corpus/styles/cold/scripts/author.ps1
@@ -0,0 +1,7 @@
+param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost
+# Snowbound makes its folder hidden, which the transfer's archive does not keep.
+$sidecar = Get-Item -LiteralPath (Join-Path $Root 'notebook\.snowbound') -Force
+$sidecar.Attributes = $sidecar.Attributes -bor [IO.FileAttributes]::Hidden
diff --git a/corpus/styles/cold/scripts/cold.ps1 b/corpus/styles/cold/scripts/cold.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f
--- /dev/null
+++ b/corpus/styles/cold/scripts/cold.ps1
@@ -0,0 +1,27 @@
+param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '')
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' }
+$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote'
+if ($CloneHost) {
+ if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) {
+ throw 'The disposable clone hostname does not match this machine.'
+ }
+ New-Item "$key\Options\Paths" -Force | Out-Null
+ New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null
+} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or
+ -not (Test-Path 'C:\one-tests\profile-original-cache')) {
+ throw 'Park the personal OneNote profile before resetting the test cache.'
+}
+$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0'
+$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root))
+if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' }
+New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null
+if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked }
+if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse }
+New-Item "$key\OpenNotebooks" | Out-Null
+New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null
diff --git a/corpus/styles/cold/scripts/read.ps1 b/corpus/styles/cold/scripts/read.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc
--- /dev/null
+++ b/corpus/styles/cold/scripts/read.ps1
@@ -0,0 +1,144 @@
+param(
+ [Parameter(Mandatory=$true)][string]$Root,
+ [int]$ExpectedPages = -1,
+ [switch]$UseCurrentCache,
+ [switch]$Pdf,
+ [switch]$KeepOpen,
+ [string]$CloneHost = ''
+)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+$notebook = Join-Path $root 'notebook'
+$output = Join-Path $root 'read'
+if (Test-Path $output) { throw 'Choose a new read destination.' }
+if ($UseCurrentCache) {
+ if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') {
+ throw 'Park the personal OneNote profile before reading test notebooks.'
+ }
+} else {
+ & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost
+}
+New-Item -ItemType Directory -Path $output | Out-Null
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+$failure = $null
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $process = Get-Process ONENOTE
+ @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion;
+ powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } |
+ ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8
+ $sections = @()
+ foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) {
+ $id = ''
+ $app.OpenHierarchy($file.FullName, '', [ref]$id, 0)
+ $sections += $id
+ }
+ $deadline = [DateTime]::UtcNow.AddSeconds(300)
+ $previous = ''
+ $lastChange = ''
+ $stableSince = [DateTime]::UtcNow
+ $settled = $false
+ do {
+ $pages = @{}
+ foreach ($section in $sections) {
+ $hierarchy = ''
+ $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
+ [xml]$xml = $hierarchy
+ foreach ($node in $xml.SelectNodes('//*[@path]')) {
+ if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'OneNote opened a section outside the copied notebook.'
+ }
+ }
+ foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) {
+ $id = $node.GetAttribute('ID')
+ $content = ''
+ $app.GetPageContent($id, [ref]$content, 1, 1)
+ $pages[$id] = $content
+ }
+ }
+ $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] }))
+ if ($signature -ne $previous) {
+ $lastChange = $previous
+ $previous = $signature
+ $stableSince = [DateTime]::UtcNow
+ }
+ if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or
+ ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and
+ ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break }
+ Start-Sleep -Milliseconds 250
+ } while ([DateTime]::UtcNow -lt $deadline)
+ if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) {
+ [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8)
+ $index = 0
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ $index++
+ }
+ $hierarchy = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1)
+ [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8)
+ throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled."
+ }
+ $index = 0
+ $payloads = @()
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ if ($Pdf) {
+ $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index)
+ $app.NavigateTo($id, '', $false)
+ $app.Publish($id, $pdfPath, 3, '')
+ if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' }
+ }
+ [xml]$page = $pages[$id]
+ foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) {
+ $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache'))
+ $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant()
+ [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes)
+ # A paragraph's file is named by its paragraph, one on the page by itself.
+ $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode }
+ $payloads += @{ page = $id; object = $owner.GetAttribute('objectID');
+ kind = $file.LocalName; name = $file.GetAttribute('preferredName');
+ sha256 = $hash; bytes = $bytes.Length }
+ }
+ $index++
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8)
+ $all = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$all, 1)
+ [xml]$finalTree = $all
+ $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique)
+ if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) {
+ throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.'
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8)
+ Write-Output "Read $($sections.Count) sections and $($pages.Count) pages."
+} catch {
+ $failure = $_
+ [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ throw
+} finally {
+ try {
+ try {
+ if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) }
+ if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) {
+ $app.CloseNotebook($notebookId, $false)
+ }
+ } catch {
+ if ($null -eq $failure) { throw }
+ [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ }
+ } finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ }
+ $app = $null
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+ if (-not $UseCurrentCache -and -not $CloneHost) {
+ Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10
+ }
+}
diff --git a/corpus/styles/cold/source.json b/corpus/styles/cold/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..3788e618b7f47dc909b3ea59855c8f75ef059db2
--- /dev/null
+++ b/corpus/styles/cold/source.json
@@ -0,0 +1,38 @@
+[
+ {
+ "path": ".snowbound/themes.json",
+ "bytes": 669,
+ "sha256": "0ede06c023cbdfa06fe307225baeb461f5a70e6ce368ae69dec9d405ef5822ac",
+ "mtime_ns": 1790821816933321351
+ },
+ {
+ "path": "Editorial.one",
+ "bytes": 12096,
+ "sha256": "0f65b622cdbeee22b146a6259074aeb4fd2b398d2bf982c7d712db31d190b6d1",
+ "mtime_ns": 1790821816932162894
+ },
+ {
+ "path": "Manuscript.one",
+ "bytes": 12120,
+ "sha256": "109d595430cf434fabcab11bfe233a34b5182a4b48f8cc5146885ee22936f636",
+ "mtime_ns": 1790821816931895020
+ },
+ {
+ "path": "Modern.one",
+ "bytes": 12000,
+ "sha256": "9ea5680d90a9b44bd036d4d50f5bcaae5b8f8ac505af4623e0ac3d0488b09ce2",
+ "mtime_ns": 1790821816932649894
+ },
+ {
+ "path": "OneNote.one",
+ "bytes": 12072,
+ "sha256": "5b019d8246db5a2728de5502d30b2a1bfe7a0a5d376c1a5d15ce98ac551ed210",
+ "mtime_ns": 1790821816932870935
+ },
+ {
+ "path": "Open Notebook.onetoc2",
+ "bytes": 2036,
+ "sha256": "9f613cf0b5db95b8809fe7d325cae9c37e8b9ae57fa3fff3b7d669649f1b416c",
+ "mtime_ns": 1790821816932392186
+ }
+]
diff --git a/corpus/styles/cold/teardown.json b/corpus/styles/cold/teardown.json
new file mode 100644
index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70
--- /dev/null
+++ b/corpus/styles/cold/teardown.json
@@ -0,0 +1 @@
+{"absent": true}
diff --git a/corpus/styles/onenote-edit/Manuscript.one b/corpus/styles/onenote-edit/Manuscript.one
new file mode 100644
index 0000000000000000000000000000000000000000..7a720c29371c07c449eaa4aa59b69703b69ecec4
Binary files /dev/null and b/corpus/styles/onenote-edit/Manuscript.one differ
diff --git a/corpus/styles/onenote-edit/Manuscript.png b/corpus/styles/onenote-edit/Manuscript.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d5a2ea13d17474553bacf704fc354d0c2441294
Binary files /dev/null and b/corpus/styles/onenote-edit/Manuscript.png differ
diff --git a/corpus/styles/onenote-edit/Manuscript.xml b/corpus/styles/onenote-edit/Manuscript.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ea02c0f6e873207e85db0593f28cdb34912b253f
--- /dev/null
+++ b/corpus/styles/onenote-edit/Manuscript.xml
@@ -0,0 +1,8 @@
+
+Heading 2 in Manuscript]]>Heading 3 in Manuscript]]>Heading 4 in Manuscript]]>Heading 6 in Manuscript]]>Quote in Manuscript]]>Normal in Manuscript]]>
diff --git a/corpus/styles/onenote/Styles.one b/corpus/styles/onenote/Styles.one
new file mode 100644
index 0000000000000000000000000000000000000000..0a1cb48a4bf475e484d3b904407b47ecc66e4d21
Binary files /dev/null and b/corpus/styles/onenote/Styles.one differ
diff --git a/corpus/styles/onenote/applied.xml b/corpus/styles/onenote/applied.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ebaf6b989d953d8a68d8667966476e11032946cf
--- /dev/null
+++ b/corpus/styles/onenote/applied.xml
@@ -0,0 +1,16 @@
+
+Heading 1 text]]>Heading 2 text]]>Heading 3 text]]>Heading 4 text]]>Heading 6 text]]>Quote text]]>After Quote]]>H1 then italic word]]>Split heading ]]>Here]]>Start enter heading]]>Explicit big bold red then h2]]>Quote then h1]]>
diff --git a/corpus/table-widths/README.md b/corpus/table-widths/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3526ae50898baab6737af3498ed45a53112bda9c
--- /dev/null
+++ b/corpus/table-widths/README.md
@@ -0,0 +1,17 @@
+# Table column widths through the editor
+
+`candidate/` is the output of `typed_and_dragged_columns_store_onenote_s_widths` in
+`crates/canvas/tests/table_widths.rs`, editing `corpus/table-tags/native`. On "Check a
+table" a three-by-three table is typed after the text a keystroke per revision, each
+storing its column's fitted width with its text; on "Edit a tagged table" a native cell
+is typed into, widening its column; on "Delete and undo" a new table's first column is
+dragged to 96 pt, locking it, and a line long enough to reach the outline's width is
+typed in the second.
+
+`cold/` is a fresh OneNote 2010 read: the typed table's columns are the widths OneNote
+gives the same table typed in it (60.76, 37.11 and 139.09 pt), the widened native column
+keeps its width, the dragged column stays 96 pt and locked, and the long line wraps in
+its column at the outline's edge. `tools/test_table_widths.py` checks this without a VM.
+Regenerate with `SNOWBOUND_TABLE_WIDTHS_EXPORT` set to a new absolute directory while
+running the test, then cold-open it with
+`tools/native_runner.py OUTPUT COLD --expected-pages 3 --collect-notebook --screenshots`.
diff --git a/corpus/table-widths/candidate/Open Notebook.onetoc2 b/corpus/table-widths/candidate/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..072b33f33b6b3076cf5757788de6993db4aa6877
Binary files /dev/null and b/corpus/table-widths/candidate/Open Notebook.onetoc2 differ
diff --git a/corpus/table-widths/candidate/Tables.one b/corpus/table-widths/candidate/Tables.one
new file mode 100644
index 0000000000000000000000000000000000000000..260450fb65f61690ac53af3aa74f5adb1a115c79
Binary files /dev/null and b/corpus/table-widths/candidate/Tables.one differ
diff --git a/corpus/table-widths/cold/commands.jsonl b/corpus/table-widths/cold/commands.jsonl
new file mode 100644
index 0000000000000000000000000000000000000000..226c9d58dd17ee4f7fc2039401d86b2b4c4187ec
--- /dev/null
+++ b/corpus/table-widths/cold/commands.jsonl
@@ -0,0 +1,3 @@
+{"command": "powershell -NoProfile -Command \"Expand-Archive -LiteralPath C:\\one-tests\\transfer.zip -DestinationPath C:\\one-tests\\runs\\capture\\notebook\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File C:\\one-tests\\read-current.ps1 -Root C:\\one-tests\\runs\\capture -CloneHost ONE-M6-F8389DC9 -ExpectedPages 3 -KeepOpen", "exit": 0, "stdout": "Read 1 sections and 3 pages.\r\n", "stderr": "", "error": null}
+{"command": "powershell -NoProfile -Command \"Compress-Archive -Force -Path C:\\one-tests\\runs\\capture\\* -DestinationPath C:\\one-tests\\captured.zip\"", "exit": 0, "stdout": "", "stderr": "", "error": null}
diff --git a/corpus/table-widths/cold/machine.json b/corpus/table-widths/cold/machine.json
new file mode 100644
index 0000000000000000000000000000000000000000..64e8beebcac7e4860ea2cba58fada86f498fada1
--- /dev/null
+++ b/corpus/table-widths/cold/machine.json
@@ -0,0 +1 @@
+{"name": "m6-f8389dc9", "hostname": "ONE-M6-F8389DC9"}
diff --git a/corpus/table-widths/cold/notebook/Open Notebook.onetoc2 b/corpus/table-widths/cold/notebook/Open Notebook.onetoc2
new file mode 100644
index 0000000000000000000000000000000000000000..e5444d85f95322e3dd910f33b09f5e50f6b1c0aa
Binary files /dev/null and b/corpus/table-widths/cold/notebook/Open Notebook.onetoc2 differ
diff --git a/corpus/table-widths/cold/notebook/Tables.one b/corpus/table-widths/cold/notebook/Tables.one
new file mode 100644
index 0000000000000000000000000000000000000000..82e9d2103d7e1c1df09833739886a95a87b6e8a8
Binary files /dev/null and b/corpus/table-widths/cold/notebook/Tables.one differ
diff --git a/corpus/table-widths/cold/read/environment.json b/corpus/table-widths/cold/read/environment.json
new file mode 100644
index 0000000000000000000000000000000000000000..526ea192b5532b90546119f83796b66912371738
--- /dev/null
+++ b/corpus/table-widths/cold/read/environment.json
@@ -0,0 +1,7 @@
+{
+ "powershell": "5.1.14409.1005",
+ "schema": "xs2010",
+ "hostname": "ONE-M6-F8389DC9",
+ "cold": true,
+ "onenote": "14.0.4763.1000"
+}
diff --git a/corpus/table-widths/cold/read/hierarchy.xml b/corpus/table-widths/cold/read/hierarchy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cfa23966f5b734adefa410b62c7c1dbedcd1a4ee
--- /dev/null
+++ b/corpus/table-widths/cold/read/hierarchy.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/table-widths/cold/read/page-000.navigation.json b/corpus/table-widths/cold/read/page-000.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..f96bf51a7b355a67f40c9bb4d1d7f1cf04129a47
--- /dev/null
+++ b/corpus/table-widths/cold/read/page-000.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Delete and undo - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/table-widths/cold/read/page-000.png b/corpus/table-widths/cold/read/page-000.png
new file mode 100644
index 0000000000000000000000000000000000000000..826b68ddc2625d9945a9997e9fe88b6647762cc6
Binary files /dev/null and b/corpus/table-widths/cold/read/page-000.png differ
diff --git a/corpus/table-widths/cold/read/page-000.xml b/corpus/table-widths/cold/read/page-000.xml
new file mode 100644
index 0000000000000000000000000000000000000000..92afc6f0b1daec063ee0737d44d19dafb45fbe35
--- /dev/null
+++ b/corpus/table-widths/cold/read/page-000.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/table-widths/cold/read/page-001.navigation.json b/corpus/table-widths/cold/read/page-001.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..c40a308d9c3c9026ea7bafe144254a80248bacb1
--- /dev/null
+++ b/corpus/table-widths/cold/read/page-001.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Edit a tagged table - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/table-widths/cold/read/page-001.png b/corpus/table-widths/cold/read/page-001.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2ad9494a5b061bc2428bbccecca13eabf28e9c0
Binary files /dev/null and b/corpus/table-widths/cold/read/page-001.png differ
diff --git a/corpus/table-widths/cold/read/page-001.xml b/corpus/table-widths/cold/read/page-001.xml
new file mode 100644
index 0000000000000000000000000000000000000000..891acef918b921931d2224a065a32e89ce2d3c71
--- /dev/null
+++ b/corpus/table-widths/cold/read/page-001.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/table-widths/cold/read/page-002.navigation.json b/corpus/table-widths/cold/read/page-002.navigation.json
new file mode 100644
index 0000000000000000000000000000000000000000..0a7b9e0ec6b6277b956d5177ea958a524786fb8b
--- /dev/null
+++ b/corpus/table-widths/cold/read/page-002.navigation.json
@@ -0,0 +1,13 @@
+{
+ "exit": 0,
+ "stdout": "",
+ "stderr": "",
+ "w": 800,
+ "h": 600,
+ "error": null,
+ "win": {
+ "title": "Check a table - Microsoft OneNote",
+ "class": "Framework::CFrame",
+ "dialog": false
+ }
+}
\ No newline at end of file
diff --git a/corpus/table-widths/cold/read/page-002.png b/corpus/table-widths/cold/read/page-002.png
new file mode 100644
index 0000000000000000000000000000000000000000..ddaa7aff31142b4501aa54f04d34a87dcef4abbe
Binary files /dev/null and b/corpus/table-widths/cold/read/page-002.png differ
diff --git a/corpus/table-widths/cold/read/page-002.xml b/corpus/table-widths/cold/read/page-002.xml
new file mode 100644
index 0000000000000000000000000000000000000000..791beb24d7b431e87abe887c2fa8bce7f2ec7c89
--- /dev/null
+++ b/corpus/table-widths/cold/read/page-002.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/corpus/table-widths/cold/read/payloads.json b/corpus/table-widths/cold/read/payloads.json
new file mode 100644
index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843
--- /dev/null
+++ b/corpus/table-widths/cold/read/payloads.json
@@ -0,0 +1,3 @@
+[
+
+]
\ No newline at end of file
diff --git a/corpus/table-widths/cold/run.json b/corpus/table-widths/cold/run.json
new file mode 100644
index 0000000000000000000000000000000000000000..bedb24582e2bed7b6cb7c416abe04d0297ec5912
--- /dev/null
+++ b/corpus/table-widths/cold/run.json
@@ -0,0 +1,18 @@
+{
+ "notebook": "/private/tmp/snowbound-tables/candidate",
+ "expected_pages": 3,
+ "author": null,
+ "author_timeout_seconds": 600,
+ "inspect": false,
+ "collect_notebook": true,
+ "base": {
+ "file": "win7-office-base.qcow2",
+ "format": "qcow2",
+ "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346",
+ "virtual_size": 68719476736
+ },
+ "scripts": {
+ "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331",
+ "read.ps1": "22bc760f955ef59bb5f0ccfd55271f8d868674084127fbe219e1048bd3777f35"
+ }
+}
diff --git a/corpus/table-widths/cold/scripts/cold.ps1 b/corpus/table-widths/cold/scripts/cold.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f
--- /dev/null
+++ b/corpus/table-widths/cold/scripts/cold.ps1
@@ -0,0 +1,27 @@
+param([Parameter(Mandatory=$true)][string]$Root, [string]$CloneHost = '')
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+if (Get-Process ONENOTE -ErrorAction SilentlyContinue) { throw 'Close OneNote before resetting its test cache.' }
+$key = 'HKCU:\Software\Microsoft\Office\14.0\OneNote'
+if ($CloneHost) {
+ if ($CloneHost -notmatch '^ONE-[A-Z0-9-]+$' -or [Environment]::MachineName -ne $CloneHost) {
+ throw 'The disposable clone hostname does not match this machine.'
+ }
+ New-Item "$key\Options\Paths" -Force | Out-Null
+ New-ItemProperty "$key\Options\Paths" -Name UnfiledNotesSection -PropertyType ExpandString -Value 'C:\one-tests\Loose.one' -Force | Out-Null
+} elseif ((Get-ItemProperty "$key\Options\Paths").UnfiledNotesSection -ne 'C:\one-tests\Loose.one' -or
+ -not (Test-Path 'C:\one-tests\profile-original-cache')) {
+ throw 'Park the personal OneNote profile before resetting the test cache.'
+}
+$cache = Join-Path $env:LOCALAPPDATA 'Microsoft\OneNote\14.0'
+$parked = Join-Path 'C:\one-tests\caches' ([IO.Path]::GetFileName($root))
+if (Test-Path $parked) { throw 'Choose a new run; its parked cache already exists.' }
+New-Item -ItemType Directory -Path 'C:\one-tests\caches' -Force | Out-Null
+if (Test-Path $cache) { Move-Item -LiteralPath $cache -Destination $parked }
+if (Test-Path "$key\OpenNotebooks") { Remove-Item "$key\OpenNotebooks" -Recurse }
+New-Item "$key\OpenNotebooks" | Out-Null
+New-ItemProperty "$key\OpenNotebooks" -Name '1' -PropertyType String -Value "$root\notebook" | Out-Null
diff --git a/corpus/table-widths/cold/scripts/read.ps1 b/corpus/table-widths/cold/scripts/read.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..a42366b32e8baaf13dae92e9472cae7a071220cc
--- /dev/null
+++ b/corpus/table-widths/cold/scripts/read.ps1
@@ -0,0 +1,144 @@
+param(
+ [Parameter(Mandatory=$true)][string]$Root,
+ [int]$ExpectedPages = -1,
+ [switch]$UseCurrentCache,
+ [switch]$Pdf,
+ [switch]$KeepOpen,
+ [string]$CloneHost = ''
+)
+Set-StrictMode -Version Latest
+$ErrorActionPreference = 'Stop'
+$root = [IO.Path]::GetFullPath($Root).TrimEnd('\')
+if ([IO.Path]::GetDirectoryName($root) -ne 'C:\one-tests\runs') {
+ throw 'Choose a run directly below C:\one-tests\runs.'
+}
+$notebook = Join-Path $root 'notebook'
+$output = Join-Path $root 'read'
+if (Test-Path $output) { throw 'Choose a new read destination.' }
+if ($UseCurrentCache) {
+ if ((Get-ItemProperty 'HKCU:\Software\Microsoft\Office\14.0\OneNote\Options\Paths').UnfiledNotesSection -ne 'C:\one-tests\Loose.one') {
+ throw 'Park the personal OneNote profile before reading test notebooks.'
+ }
+} else {
+ & "$PSScriptRoot\cold-current.ps1" -Root $root -CloneHost $CloneHost
+}
+New-Item -ItemType Directory -Path $output | Out-Null
+$app = New-Object -ComObject OneNote.Application
+$notebookId = ''
+$failure = $null
+try {
+ $app.OpenHierarchy($notebook, '', [ref]$notebookId, 0)
+ $process = Get-Process ONENOTE
+ @{ hostname = [Environment]::MachineName; onenote = $process.MainModule.FileVersionInfo.FileVersion;
+ powershell = $PSVersionTable.PSVersion.ToString(); schema = 'xs2010'; cold = (-not $UseCurrentCache.IsPresent) } |
+ ConvertTo-Json | Set-Content (Join-Path $output 'environment.json') -Encoding UTF8
+ $sections = @()
+ foreach ($file in @(Get-ChildItem $notebook -Recurse | Where-Object { $_.Extension -eq '.one' })) {
+ $id = ''
+ $app.OpenHierarchy($file.FullName, '', [ref]$id, 0)
+ $sections += $id
+ }
+ $deadline = [DateTime]::UtcNow.AddSeconds(300)
+ $previous = ''
+ $lastChange = ''
+ $stableSince = [DateTime]::UtcNow
+ $settled = $false
+ do {
+ $pages = @{}
+ foreach ($section in $sections) {
+ $hierarchy = ''
+ $app.GetHierarchy($section, 4, [ref]$hierarchy, 1)
+ [xml]$xml = $hierarchy
+ foreach ($node in $xml.SelectNodes('//*[@path]')) {
+ if (-not $node.GetAttribute('path').StartsWith("$notebook\", [StringComparison]::OrdinalIgnoreCase)) {
+ throw 'OneNote opened a section outside the copied notebook.'
+ }
+ }
+ foreach ($node in $xml.SelectNodes('//*[local-name()="Page"]')) {
+ $id = $node.GetAttribute('ID')
+ $content = ''
+ $app.GetPageContent($id, [ref]$content, 1, 1)
+ $pages[$id] = $content
+ }
+ }
+ $signature = [String]::Join('|', @($pages.Keys | Sort-Object | ForEach-Object { $_ + $pages[$_] }))
+ if ($signature -ne $previous) {
+ $lastChange = $previous
+ $previous = $signature
+ $stableSince = [DateTime]::UtcNow
+ }
+ if ((($ExpectedPages -ge 0 -and $pages.Count -eq $ExpectedPages) -or
+ ($ExpectedPages -lt 0 -and $pages.Count -gt 0)) -and
+ ([DateTime]::UtcNow - $stableSince).TotalSeconds -ge 2) { $settled = $true; break }
+ Start-Sleep -Milliseconds 250
+ } while ([DateTime]::UtcNow -lt $deadline)
+ if (-not $settled -or ($ExpectedPages -ge 0 -and $pages.Count -ne $ExpectedPages) -or ($ExpectedPages -lt 0 -and $pages.Count -eq 0)) {
+ [IO.File]::WriteAllText((Join-Path $output 'previous-signature.txt'), $lastChange, [Text.Encoding]::UTF8)
+ $index = 0
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('unsettled-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ $index++
+ }
+ $hierarchy = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$hierarchy, 1)
+ [IO.File]::WriteAllText((Join-Path $output 'unsettled-hierarchy.xml'), $hierarchy, [Text.Encoding]::UTF8)
+ throw "Expected $ExpectedPages stable pages; OneNote returned $($pages.Count), settled=$settled."
+ }
+ $index = 0
+ $payloads = @()
+ foreach ($id in @($pages.Keys | Sort-Object)) {
+ [IO.File]::WriteAllText((Join-Path $output ('page-{0:d3}.xml' -f $index)), $pages[$id], [Text.Encoding]::UTF8)
+ if ($Pdf) {
+ $pdfPath = Join-Path $output ('page-{0:d3}.pdf' -f $index)
+ $app.NavigateTo($id, '', $false)
+ $app.Publish($id, $pdfPath, 3, '')
+ if (-not (Test-Path $pdfPath) -or (Get-Item $pdfPath).Length -eq 0) { throw 'OneNote did not publish the page PDF.' }
+ }
+ [xml]$page = $pages[$id]
+ foreach ($file in $page.SelectNodes('//*[local-name()="InsertedFile" or local-name()="MediaFile"]')) {
+ $bytes = [IO.File]::ReadAllBytes($file.GetAttribute('pathCache'))
+ $hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '').ToLowerInvariant()
+ [IO.File]::WriteAllBytes((Join-Path $output ($hash + '.attachment')), $bytes)
+ # A paragraph's file is named by its paragraph, one on the page by itself.
+ $owner = if ($file.ParentNode.LocalName -eq 'Page') { $file } else { $file.ParentNode }
+ $payloads += @{ page = $id; object = $owner.GetAttribute('objectID');
+ kind = $file.LocalName; name = $file.GetAttribute('preferredName');
+ sha256 = $hash; bytes = $bytes.Length }
+ }
+ $index++
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'payloads.json'), (ConvertTo-Json -InputObject $payloads -Depth 4), [Text.Encoding]::UTF8)
+ $all = ''
+ $app.GetHierarchy($notebookId, 4, [ref]$all, 1)
+ [xml]$finalTree = $all
+ $finalIds = @($finalTree.SelectNodes('//*[local-name()="Page"]') | ForEach-Object { $_.GetAttribute('ID') } | Sort-Object -Unique)
+ if ($finalIds.Count -ne $pages.Count -or @($finalIds | Where-Object { -not $pages.ContainsKey($_) }).Count -ne 0) {
+ throw 'The notebook hierarchy changed while collecting page evidence; repeat the cold read.'
+ }
+ [IO.File]::WriteAllText((Join-Path $output 'hierarchy.xml'), $all, [Text.Encoding]::UTF8)
+ Write-Output "Read $($sections.Count) sections and $($pages.Count) pages."
+} catch {
+ $failure = $_
+ [IO.File]::WriteAllText((Join-Path $output 'failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ throw
+} finally {
+ try {
+ try {
+ if ($notebookId -and $CloneHost) { $app.SyncHierarchy($notebookId) }
+ if ($notebookId -and -not $KeepOpen -and (-not $UseCurrentCache -or $CloneHost)) {
+ $app.CloseNotebook($notebookId, $false)
+ }
+ } catch {
+ if ($null -eq $failure) { throw }
+ [IO.File]::WriteAllText((Join-Path $output 'cleanup-failure.txt'), ($_ | Out-String), [Text.Encoding]::UTF8)
+ }
+ } finally {
+ [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
+ }
+ $app = $null
+ [GC]::Collect()
+ [GC]::WaitForPendingFinalizers()
+ if (-not $UseCurrentCache -and -not $CloneHost) {
+ Get-Process ONENOTE -ErrorAction SilentlyContinue | Wait-Process -Timeout 10
+ }
+}
diff --git a/corpus/table-widths/cold/source.json b/corpus/table-widths/cold/source.json
new file mode 100644
index 0000000000000000000000000000000000000000..49ab44891d129a5d739922182daa417eb9f78fcc
--- /dev/null
+++ b/corpus/table-widths/cold/source.json
@@ -0,0 +1,14 @@
+[
+ {
+ "path": "Open Notebook.onetoc2",
+ "bytes": 1604,
+ "sha256": "78250a5c9a50d89ba0b9a0cb725c8afe0c9cbc538a8f39e8a3cde75d2aebed65",
+ "mtime_ns": 1790820308278651723
+ },
+ {
+ "path": "Tables.one",
+ "bytes": 35192,
+ "sha256": "b45ea79ddcb9c9f30992e9beee4919993578c295cc81605ca3d97fbbfc4d2eac",
+ "mtime_ns": 1790820308259896484
+ }
+]
diff --git a/corpus/table-widths/cold/teardown.json b/corpus/table-widths/cold/teardown.json
new file mode 100644
index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70
--- /dev/null
+++ b/corpus/table-widths/cold/teardown.json
@@ -0,0 +1 @@
+{"absent": true}
diff --git a/crates/canvas/examples/print_pdf.rs b/crates/canvas/examples/print_pdf.rs
index a2615124b8b16e09fdfc558c48d7ed16c3208190..7f1f00d66da4f4a8c8dacfa3cb08759e5d10daa0 100644
--- a/crates/canvas/examples/print_pdf.rs
+++ b/crates/canvas/examples/print_pdf.rs
@@ -33,8 +33,16 @@ fn main() -> Result<(), Box> {
.file_stem()
.and_then(|stem| stem.to_str())
.unwrap_or_default();
- let paper = if a4 { print::A4 } else { print::LETTER };
- let pdf = print::pdf(pages, &mut TextEngine::default(), paper, name)?;
+ let setup = print::Setup {
+ paper: if a4 { print::A4 } else { print::LETTER },
+ fit_width: true,
+ footer: print::Footer::SectionAndPage,
+ };
+ let pdf = print::pdf(
+ vec![(name.to_owned(), pages)],
+ &mut TextEngine::default(),
+ &setup,
+ )?;
fs::write(destination, pdf)?;
Ok(())
}
diff --git a/crates/canvas/examples/scratch_eqref.rs b/crates/canvas/examples/scratch_eqref.rs
deleted file mode 100644
index 9f5aab70bf7be18cbc32f13a60e08fa50a84b333..0000000000000000000000000000000000000000
--- a/crates/canvas/examples/scratch_eqref.rs
+++ /dev/null
@@ -1,667 +0,0 @@
-//! Every place the canvas shows a page's content as a placeholder, draws it read-only, or
-//! refuses an ordinary edit as content it cannot edit, across the active pages of every
-//! section under the given directories, grouped by kind as Markdown. Identical sections
-//! count once.
-//!
-//! `cargo run --release -p canvas --features gpu --example unsupported_inventory -- corpus`
-
-use canvas::{
- document::TextPosition,
- editor::{CanvasEditor, EditorError, TextOutline},
- layout::{LayoutError, TextEngine},
- outline::Arrange,
-};
-use onestore::{
- Arena, ExGuid, Section,
- page::{
- Image, Outline, Page, PageObject, PageParagraph, Paragraph, ParagraphContent,
- text::{Affinity, EditError},
- },
-};
-use std::{
- collections::{BTreeMap, BTreeSet, hash_map::DefaultHasher},
- hash::{Hash, Hasher},
- path::{Path, PathBuf},
-};
-
-#[derive(Default)]
-struct Inventory {
- /// Kind to the places it occurs, as `section — page`.
- kinds: BTreeMap<(Stage, String), Vec>,
- sections: usize,
- pages: usize,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
-enum Stage {
- /// Drawn as an "Unsupported content" (or "Image unavailable") box.
- Placeholder,
- /// Drawn as stored, but the editor cannot hold it.
- ReadOnly,
- /// An ordinary edit the editor refuses as content it cannot edit.
- Refused,
- /// The page or section does not open at all.
- Failure,
-}
-
-impl Inventory {
- fn add(&mut self, stage: Stage, kind: impl Into, place: &str) {
- self.kinds
- .entry((stage, kind.into()))
- .or_default()
- .push(place.to_owned());
- }
-}
-
-/// MS-ONE's names for the object kinds it documents, and the observed undocumented ones.
-fn jcid_name(jcid: u32) -> String {
- let name = match jcid {
- 0x60007 => "section",
- 0x60008 => "page series",
- 0x6000b => "page",
- 0x6000c => "outline",
- 0x6000d => "outline element",
- 0x6000e => "rich text",
- 0x60011 => "picture",
- 0x60012 => "number list",
- 0x60014 => "ink container",
- 0x60019 => "outline group",
- 0x60022 => "table",
- 0x60023 => "table row",
- 0x60024 => "table cell",
- 0x6002c => "title",
- 0x60035 => "embedded file",
- 0x60037 => "page manifest",
- 0x6003c => "version history",
- 0x6003d => "version proxy",
- _ => "undocumented",
- };
- format!("jcid 0x{jcid:05x} ({name})")
-}
-
-fn unsupported_error(error: &EditorError) -> bool {
- matches!(
- error,
- EditorError::Layout(LayoutError::UnsupportedContent)
- | EditorError::Edit(EditError::UnsupportedContent)
- )
-}
-
-/// What a text paragraph holds that bears on editing it.
-fn traits(text: &Paragraph) -> Vec<&'static str> {
- let spans = text.spans();
- let mut traits = Vec::new();
- if text.text().contains('\u{fffc}')
- || spans.iter().any(|s| s.format.embedded_object == Some(true))
- {
- traits.push("embedded object in text");
- }
- if spans.iter().any(|s| s.format.math == Some(true)) {
- traits.push("equation");
- }
- if spans.iter().any(|s| s.format.hyperlink == Some(true)) {
- traits.push("link");
- }
- if spans.iter().any(|s| s.format.hidden == Some(true)) {
- traits.push("hidden text");
- }
- if text.text().is_empty() {
- traits.push("empty");
- }
- traits
-}
-
-/// Why `node`, alone in an outline, does not lay out as content the canvas understands.
-fn culprit(
- engine: &mut TextEngine,
- outline: &Outline,
- node: &PageParagraph,
- page: &Page,
-) -> Option {
- let alone = Outline {
- paragraphs: vec![PageParagraph {
- parent: None,
- level: 1,
- ..node.clone()
- }],
- unsupported: Vec::new(),
- ..outline.clone()
- };
- match alone.layout(engine, &page.definitions) {
- Err(LayoutError::UnsupportedContent) => {}
- _ => return None,
- }
- Some(match &node.content {
- ParagraphContent::Unsupported(u) => format!("holds {}", jcid_name(u.jcid)),
- ParagraphContent::Table(table) if !table.tags.is_empty() => "table with note tags".into(),
- ParagraphContent::Table(table) => {
- let cells = || table.rows.iter().flat_map(|row| &row.cells);
- if let Some(cell) = cells().find(|cell| !cell.unsupported.is_empty()) {
- format!(
- "table cell holding {}",
- cell.unsupported
- .iter()
- .map(|u| jcid_name(u.jcid))
- .collect::>()
- .join(", ")
- )
- } else {
- let inner = cells()
- .flat_map(|cell| &cell.paragraphs)
- .find_map(|inner| culprit(engine, outline, inner, page));
- format!(
- "table cell: {}",
- inner.unwrap_or_else(|| "nested content".into())
- )
- }
- }
- ParagraphContent::Image(_) => "picture without a size".into(),
- ParagraphContent::Ink(_) => "handwriting without strokes".into(),
- ParagraphContent::Attachment(_) => "file".into(),
- ParagraphContent::Text(text) => {
- let lists: Vec = node
- .lists
- .iter()
- .filter_map(|id| page.definitions.get(id))
- .map(|definition| format!("{:?}", definition.kind))
- .collect();
- if !lists.is_empty() {
- format!("list marker {}", lists.join(", "))
- } else if text
- .text
- .spans()
- .iter()
- .any(|s| s.format.math == Some(true))
- {
- "equation the math layout does not parse".into()
- } else {
- format!("text ({})", traits(&text.text).join(", "))
- }
- }
- })
-}
-
-/// Why an outline the page shows is not editable.
-fn diagnose(engine: &mut TextEngine, outline: &Outline, page: &Page) -> Vec {
- let mut reasons: Vec = outline
- .unsupported
- .iter()
- .map(|u| format!("outline child {}", jcid_name(u.jcid)))
- .collect();
- reasons.extend(
- outline
- .paragraphs
- .iter()
- .filter_map(|node| culprit(engine, outline, node, page)),
- );
- if reasons.is_empty() {
- let text = outline.paragraphs.iter().any(|node| {
- node.text().is_some()
- || matches!(&node.content, ParagraphContent::Table(t)
- if t.rows.iter().flat_map(|r| &r.cells).any(|c| !c.paragraphs.is_empty()))
- });
- reasons.push(if text {
- match TextOutline::from_outline(engine, outline, &page.definitions) {
- Err(error) => format!("editor: {error}"),
- Ok(_) => "outline on its own is editable".into(),
- }
- } else {
- let kinds: BTreeSet<&str> = outline
- .paragraphs
- .iter()
- .map(|node| match node.content {
- ParagraphContent::Image(_) => "pictures",
- ParagraphContent::Attachment(_) => "files",
- ParagraphContent::Ink(_) => "handwriting",
- _ => "other",
- })
- .collect();
- format!(
- "no paragraph of text (only {})",
- kinds.into_iter().collect::>().join(", ")
- )
- });
- }
- reasons
-}
-
-/// A picture that draws as "Image unavailable": no data, or (with `--features gpu`) data
-/// the renderer does not decode, named by its leading bytes.
-fn picture(image: &Image, at: &str, inventory: &mut Inventory, place: &str) {
- let Some(bytes) = &image.bytes else {
- return inventory.add(
- Stage::Placeholder,
- format!("{at} picture without data"),
- place,
- );
- };
- #[cfg(feature = "gpu")]
- if [Some(bytes), image.display.as_ref()]
- .into_iter()
- .flatten()
- .all(|bytes| draw::RasterImage::measure(bytes).is_err())
- {
- let format = match &bytes[..bytes.len().min(4)] {
- [0x50, 0x4b, 3, 4] => "a zip package (XPS printout)",
- [1, 0, 0, 0] => "EMF",
- [0xd7, 0xcd, 0xc6, 0x9a] | [1, 0, 9, 0] => "WMF",
- [b'B', b'M', ..] => "BMP",
- _ => "an unknown format",
- };
- inventory.add(
- Stage::Placeholder,
- format!("{at} picture stored as {format}"),
- place,
- );
- }
- #[cfg(not(feature = "gpu"))]
- let _ = bytes;
-}
-
-/// Pictures that draw as "Image unavailable" and objects the model keeps aside.
-fn scan_paragraphs(nodes: &[PageParagraph], at: &str, inventory: &mut Inventory, place: &str) {
- for node in nodes {
- match &node.content {
- ParagraphContent::Unsupported(u) => inventory.add(
- Stage::Placeholder,
- format!("{at} paragraph holding {}", jcid_name(u.jcid)),
- place,
- ),
- ParagraphContent::Image(image) => picture(image, at, inventory, place),
- ParagraphContent::Table(table) => {
- for cell in table.rows.iter().flat_map(|row| &row.cells) {
- for u in &cell.unsupported {
- inventory.add(
- Stage::Placeholder,
- format!("table cell child {}", jcid_name(u.jcid)),
- place,
- );
- }
- scan_paragraphs(&cell.paragraphs, "table cell", inventory, place);
- }
- }
- _ => {}
- }
- }
-}
-
-/// Ordinary edits at every text paragraph of every editable outline: typing at its start,
-/// middle and end, Enter, Backspace at its start, Delete at its end, and deleting from its
-/// middle into the next.
-fn probe(
- editor: &mut CanvasEditor,
- engine: &mut TextEngine,
- inventory: &mut Inventory,
- place: &str,
-) {
- let ids: Vec = editor.outlines().iter().map(|o| o.id).collect();
- for id in ids {
- if editor.focus_outline(id).is_err() {
- continue;
- }
- let document = editor.active_outline().document();
- let containers = containers(document.nodes());
- let leaves: Vec<(Option, Paragraph)> = document
- .text_nodes()
- .map(|node| {
- (
- containers.get(&node.id).copied().flatten(),
- node.text().unwrap().text.clone(),
- )
- })
- .collect();
- for (index, (container, text)) in leaves.iter().enumerate() {
- let length = text.text().encode_utf16().count() as u32;
- // The middle of what shows, where a click could put the caret.
- let Ok(projection) = text.project() else {
- continue;
- };
- let shown = projection.text();
- let middle = shown
- .text()
- .char_indices()
- .map(|(at, _)| at)
- .nth(shown.text().chars().count() / 2)
- .and_then(|at| shown.utf16_offset(at).ok())
- .and_then(|at| projection.source_offset(at, Affinity::Downstream).ok())
- .unwrap_or(length);
- let at = |offset| TextPosition {
- paragraph: index,
- offset,
- };
- let next = leaves.get(index + 1);
- let mut probes: Vec<(&str, [TextPosition; 2], Probe)> = vec![
- ("typing at a paragraph's start", [at(0); 2], Probe::Type),
- ("typing inside a paragraph", [at(middle); 2], Probe::Type),
- ("typing at a paragraph's end", [at(length); 2], Probe::Type),
- ("Enter inside a paragraph", [at(middle); 2], Probe::Enter),
- (
- "Delete at a paragraph's end",
- [at(length); 2],
- Probe::Delete,
- ),
- ];
- if index > 0 {
- probes.push((
- "Backspace at a paragraph's start",
- [at(0); 2],
- Probe::Backspace,
- ));
- }
- if let Some((_, next_text)) = next {
- let end = next_text.text().encode_utf16().count() as u32 / 2;
- probes.push((
- "deleting a selection into the next paragraph",
- [
- at(middle),
- TextPosition {
- paragraph: index + 1,
- offset: end,
- },
- ],
- Probe::Backspace,
- ));
- }
- for (name, positions, kind) in probes {
- if editor.select(positions.into()).is_err() {
- continue;
- }
- let result = match kind {
- Probe::Type => editor.insert(engine, "x").map(|()| true),
- Probe::Enter => editor.enter(engine, false).map(|()| true),
- Probe::Delete => editor.delete(engine, false),
- Probe::Backspace => editor.delete(engine, true),
- };
- match result {
- Ok(true) => {
- editor.undo(engine).ok();
- }
- Ok(false) => {}
- Err(error) if unsupported_error(&error) => {
- let show = |t: &Paragraph, o: u32| {
- let b = t.byte_offset(o).unwrap_or(0);
- let v = |s: &str| s.replace('\u{fdd0}', "⟨").replace('\u{fdef}', "⟩").replace('\u{fdee}', "|").replace('\u{fffc}', "¤");
- format!("{}‸{}", v(&t.text()[..b]), v(&t.text()[b..]))
- };
- let a = show(text, positions[0].offset);
- let b = if positions[1].paragraph != index { show(&leaves[positions[1].paragraph].1, positions[1].offset) } else { String::new() };
- eprintln!("REFUSED {name} @ {place}\n A: {a}\n B: {b}");
- let crossing = positions[0].paragraph != positions[1].paragraph
- || name.starts_with("Backspace")
- || name.starts_with("Delete");
- let neighbour = match name {
- n if n.starts_with("Backspace") => leaves.get(index - 1),
- _ if crossing => next,
- _ => None,
- };
- let mut why: Vec<&str> = traits(text);
- if let Some((other, other_text)) = neighbour {
- if other != container {
- why.push("across a table cell boundary");
- }
- why.extend(traits(other_text).into_iter().map(|t| match t {
- "embedded object in text" => "next to an embedded object",
- "equation" => "next to an equation",
- "link" => "next to a link",
- t => t,
- }));
- }
- if container.is_some() {
- why.push("in a table cell");
- }
- why.sort();
- why.dedup();
- inventory.add(
- Stage::Refused,
- format!("{name} [{}]", why.join(", ")),
- place,
- );
- }
- Err(_) => {}
- }
- }
- }
- }
-}
-
-#[derive(Clone, Copy)]
-enum Probe {
- Type,
- Enter,
- Delete,
- Backspace,
-}
-
-/// Each paragraph's table cell, `None` at the outline's top.
-fn containers(nodes: &[PageParagraph]) -> BTreeMap> {
- let mut map = BTreeMap::new();
- let mut pending = vec![(None, nodes)];
- while let Some((container, nodes)) = pending.pop() {
- for node in nodes {
- map.insert(node.id, container);
- if let ParagraphContent::Table(table) = &node.content {
- for cell in table.rows.iter().flat_map(|row| &row.cells) {
- pending.push((Some(cell.id), cell.paragraphs.as_slice()));
- }
- }
- }
- }
- map
-}
-
-fn page(engine: &mut TextEngine, page: Page, place: &str, inventory: &mut Inventory) {
- for object in &page.objects {
- match object {
- PageObject::Unsupported(u) => inventory.add(
- Stage::Placeholder,
- format!("page object {}", jcid_name(u.jcid)),
- place,
- ),
- PageObject::Image(image) => picture(image, "page", inventory, place),
- PageObject::Outline(outline) => {
- for u in &outline.unsupported {
- inventory.add(
- Stage::Placeholder,
- format!("outline child {}", jcid_name(u.jcid)),
- place,
- );
- }
- scan_paragraphs(&outline.paragraphs, "outline", inventory, place);
- }
- PageObject::Title(title) => {
- for outline in &title.outlines {
- scan_paragraphs(&outline.paragraphs, "title", inventory, place);
- }
- }
- _ => {}
- }
- }
- let mut editor = match CanvasEditor::from_page(page.clone(), engine) {
- Ok(editor) => editor,
- Err(error) => {
- inventory.add(
- Stage::Failure,
- format!("page does not open: {error}"),
- place,
- );
- return;
- }
- };
- for object in &page.objects {
- let (outlines, title) = match object {
- PageObject::Outline(outline) => (std::slice::from_ref(outline), None),
- PageObject::Title(title) => (title.outlines.as_slice(), Some(title)),
- _ => continue,
- };
- for outline in outlines {
- if editor.has_page_outline(outline.id)
- || editor.outlines().iter().any(|o| o.id == outline.id)
- {
- continue;
- }
- if let Some(title) = title {
- if title.date == Some(outline.id) {
- if editor.date().is_none() {
- inventory.add(Stage::ReadOnly, "page date the date editor declines", place);
- }
- continue;
- }
- if !outline.title {
- continue;
- }
- }
- let placeholder = matches!(
- outline.layout(engine, &page.definitions),
- Err(LayoutError::UnsupportedContent)
- );
- let at = if title.is_some() { "title" } else { "outline" };
- for reason in diagnose(engine, outline, &page) {
- let stage = if placeholder {
- Stage::Placeholder
- } else {
- Stage::ReadOnly
- };
- let whole = if placeholder {
- format!("whole {at} as a box: {reason}")
- } else {
- format!("{at} read-only: {reason}")
- };
- inventory.add(stage, whole, place);
- }
- }
- }
- probe(&mut editor, engine, inventory, place);
-}
-
-fn section(engine: &mut TextEngine, path: &Path, root: &Path, inventory: &mut Inventory) {
- let Ok(image) = std::fs::read(path) else {
- return;
- };
- let name = path
- .strip_prefix(root)
- .unwrap_or(path)
- .display()
- .to_string();
- let arena = Arena::default();
- let mut section = match Section::open(&arena, image) {
- Ok(section) => section,
- Err(error) => {
- inventory.add(
- Stage::Failure,
- format!("section does not open: {}", error.message),
- &name,
- );
- return;
- }
- };
- inventory.sections += 1;
- let pages = match section.pages() {
- Ok(pages) => pages,
- Err(error) => {
- inventory.add(
- Stage::Failure,
- format!("page list does not read: {}", error.message),
- &name,
- );
- return;
- }
- };
- for (space, title, _) in pages {
- inventory.pages += 1;
- let place = format!("{name} — {title}");
- match section.page(space) {
- Ok(model) => page(engine, model, &place, inventory),
- Err(error) => inventory.add(
- Stage::Failure,
- format!("page does not read: {}", error.message),
- &place,
- ),
- }
- }
-}
-
-fn sections(directory: &Path, out: &mut Vec) {
- let Ok(entries) = std::fs::read_dir(directory) else {
- return;
- };
- let mut entries: Vec = entries.flatten().map(|entry| entry.path()).collect();
- entries.sort();
- for path in entries {
- if path.is_dir() {
- sections(&path, out);
- } else if path.extension().is_some_and(|e| e == "one") {
- out.push(path);
- }
- }
-}
-
-fn main() {
- let roots: Vec = std::env::args().skip(1).map(PathBuf::from).collect();
- let mut engine = TextEngine::default();
- let mut inventory = Inventory::default();
- let mut seen = BTreeSet::new();
- let mut duplicates = 0;
- for root in &roots {
- let mut paths = Vec::new();
- sections(root, &mut paths);
- for path in paths {
- let Ok(bytes) = std::fs::read(&path) else {
- continue;
- };
- let mut hasher = DefaultHasher::new();
- bytes.hash(&mut hasher);
- if !seen.insert(hasher.finish()) {
- duplicates += 1;
- continue;
- }
- section(&mut engine, &path, root, &mut inventory);
- }
- }
- println!("# Unsupported content inventory\n");
- println!(
- "{} distinct sections ({duplicates} identical copies skipped), {} pages, under {}.\n",
- inventory.sections,
- inventory.pages,
- roots
- .iter()
- .map(|r| format!("`{}`", r.display()))
- .collect::>()
- .join(", ")
- );
- let headings = [
- (Stage::Placeholder, "Drawn as a placeholder box"),
- (Stage::ReadOnly, "Drawn as stored, read-only"),
- (
- Stage::Refused,
- "Edits refused as content the editor cannot edit",
- ),
- (Stage::Failure, "Does not open"),
- ];
- for (stage, heading) in headings {
- let mut rows: Vec<(&String, &Vec)> = inventory
- .kinds
- .iter()
- .filter(|((s, _), _)| *s == stage)
- .map(|((_, kind), places)| (kind, places))
- .collect();
- rows.sort_by(|a, b| b.1.len().cmp(&a.1.len()).then(a.0.cmp(b.0)));
- println!("## {heading}\n");
- if rows.is_empty() {
- println!("None.\n");
- continue;
- }
- println!("| Kind | Count | Pages | Examples |\n| --- | ---: | ---: | --- |");
- for (kind, places) in rows {
- let pages: BTreeSet<&String> = places.iter().collect();
- let examples: Vec = pages.iter().take(3).map(|p| format!("`{p}`")).collect();
- println!(
- "| {} | {} | {} | {} |",
- kind.replace('|', "\\|"),
- places.len(),
- pages.len(),
- examples.join("
").replace('|', "\\|")
- );
- }
- println!();
- }
-}
diff --git a/crates/canvas/examples/scratch_files.rs b/crates/canvas/examples/scratch_files.rs
deleted file mode 100644
index 6c458d6a182c00b8e003cf3e211a47fd58620915..0000000000000000000000000000000000000000
--- a/crates/canvas/examples/scratch_files.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use onestore::{RevisionIndex, Store, document::{Document, Kind}};
-fn main() {
- let bytes = std::fs::read(std::env::args().nth(1).unwrap()).unwrap();
- let store = Store::parse(&bytes).unwrap();
- let index = RevisionIndex::parse(&store).unwrap();
- let document = Document::parse(&index).unwrap();
- for (space, _) in document.pages().unwrap() {
- let revision = document.active(space).unwrap();
- for (id, node) in &revision.nodes {
- if let Kind::File { extension, payload, .. } = &node.kind {
- println!("{id:?} {extension} {:?}", payload.map(|p| (p.len(), &p[..4.min(p.len())])));
- }
- }
- }
-}
diff --git a/crates/canvas/examples/scratch_runs.rs b/crates/canvas/examples/scratch_runs.rs
deleted file mode 100644
index 33b31e9ebba3048c62ffe441927688290c8187e5..0000000000000000000000000000000000000000
--- a/crates/canvas/examples/scratch_runs.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use onestore::{Arena, Section, page::{PageObject}};
-fn main() {
- let path = std::env::args().nth(1).unwrap();
- let want = std::env::args().nth(2).unwrap_or_default();
- let arena = Arena::default();
- let mut section = Section::open(&arena, std::fs::read(path).unwrap()).unwrap();
- for (space, _, _) in section.pages().unwrap() {
- let page = section.page(space).unwrap();
- for object in &page.objects {
- let PageObject::Outline(outline) = object else { continue };
- for p in &outline.paragraphs {
- let Some(text) = p.text() else { continue };
- if !text.text.text().contains(want.as_str()) { continue }
- let mut start = 0;
- println!("-- {:?}", p.id);
- for span in text.text.spans() {
- println!(" {:?} math={:?} obj={:?} emb={:?}", &text.text.text()[start..span.end], span.format.math, span.format.math_object, span.format.embedded_object);
- start = span.end;
- }
- }
- }
- }
-}
diff --git a/crates/canvas/examples/unsupported_inventory.rs b/crates/canvas/examples/unsupported_inventory.rs
index 2443ce8f43c091f1c86fdeedb903eaddf342e558..1eddfb8e8978911e343a06355216be265ac1522e 100644
--- a/crates/canvas/examples/unsupported_inventory.rs
+++ b/crates/canvas/examples/unsupported_inventory.rs
@@ -468,6 +468,25 @@ fn page(engine: &mut TextEngine, page: Page, place: &str, inventory: &mut Invent
_ => {}
}
}
+ // Pictures with pixels the scene still shows as placeholders, for want of a size or the
+ // like; undecodable ones are counted above.
+ #[cfg(feature = "gpu")]
+ if let Ok((scene, shown)) = canvas::gpu::page::PageScene::from_page(page.clone(), engine) {
+ for object in scene.read_only(Some(&shown)) {
+ if let PageObject::Image(image) = &object.source
+ && [image.bytes.as_ref(), image.display.as_ref()]
+ .into_iter()
+ .flatten()
+ .any(|bytes| draw::RasterImage::measure(bytes).is_ok())
+ {
+ inventory.add(
+ Stage::Placeholder,
+ "page picture with pixels drawn as \"Image unavailable\"",
+ place,
+ );
+ }
+ }
+ }
let mut editor = match CanvasEditor::from_page(page.clone(), engine) {
Ok(editor) => editor,
Err(error) => {
diff --git a/crates/canvas/src/editor.rs b/crates/canvas/src/editor.rs
index e99e394f84450d108456ec3dac63641cc8b926b9..41d0f75a8a86709d7f77d612090af2febf4b8222 100644
--- a/crates/canvas/src/editor.rs
+++ b/crates/canvas/src/editor.rs
@@ -36,7 +36,8 @@ mod recording;
mod space;
mod table;
pub use format::{
- Alignment, BULLET_LIBRARY, FormatState, Formatting, ListStyle, NUMBER_LIBRARY, NoteTag, Toggle,
+ Alignment, BULLET_LIBRARY, DefaultFont, FormatState, Formatting, ListStyle, NUMBER_LIBRARY,
+ NoteTag, Toggle,
};
pub use ink::{FAVORITES, Pen};
pub use link::{Link, shown_urls};
@@ -81,6 +82,47 @@ pub struct Selection {
pub affinities: [Affinity; 2],
}
+/// A piece of pasted content, in the order it comes.
+pub enum Piece {
+ Text(String),
+ /// An encoded picture and its size in points.
+ Picture(Vec, [f32; 2]),
+ /// A picture still on its way, which ends the paragraph and goes in where
+ /// [`CanvasEditor::paste_pieces`] says once it arrives.
+ Awaited,
+}
+
+/// Where an awaited picture goes: before paragraph `before` of outline `outline`.
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub struct Awaited {
+ outline: ExGuid,
+ before: ExGuid,
+}
+
+/// A new picture of encoded `bytes`, `size` points large.
+pub(crate) fn picture(
+ bytes: Vec,
+ size: [f32; 2],
+) -> Result {
+ Ok(onestore::page::Image {
+ id: onestore::page::text::new_id()?,
+ layout: onestore::document::Layout {
+ max_width: Some(size[0]),
+ max_height: Some(size[1]),
+ ..Default::default()
+ },
+ size: Some(size),
+ bytes: Some(bytes.into()),
+ display: None,
+ alt: None,
+ background: false,
+ printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
+ })
+}
+
/// What OneNote 2010's Ctrl+A selects beyond text: the focused outline as an object, then every
/// body outline on the page.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -102,7 +144,7 @@ pub struct CanvasEditor {
pub(crate) objects: Vec,
date: Option,
outlines: Vec,
- definitions: BTreeMap,
+ pub(crate) definitions: BTreeMap,
header: PageHeader,
active: Focus,
undo: Vec,
@@ -130,6 +172,12 @@ pub struct CanvasEditor {
/// and whether it is stored: the first op that reaches it stores it, and undoing back to it
/// empty and alone removes it again, as OneNote 2010's undo does.
provisional: BTreeMap,
+ /// What new outlines' text is set in; `PageView::replace` hands it to the next page's
+ /// editor.
+ pub default_font: DefaultFont,
+ /// The paragraph styles the page's theme gives, by stored name: new text takes its `p`,
+ /// and Enter a NextStyle the page lacks from here.
+ pub styles: BTreeMap,
}
/// Imported page state the editable content does not carry.
@@ -847,6 +895,11 @@ enum History {
index: usize,
ink: Option>,
},
+ /// Gives a picture or file on the page back the note tags it had.
+ Tags {
+ object: ExGuid,
+ tags: Vec,
+ },
/// Gives a drawing back the strokes it had.
Strokes {
ink: ExGuid,
@@ -912,6 +965,8 @@ impl CanvasEditor {
recording: None,
ink_drag: None,
provisional: BTreeMap::new(),
+ default_font: DefaultFont::default(),
+ styles: BTreeMap::new(),
})
}
@@ -1110,6 +1165,7 @@ impl CanvasEditor {
History::Date(_) => date_changed,
History::Paper { .. } => false,
History::Image { .. }
+ | History::Tags { .. }
| History::Picture { .. }
| History::File { .. }
| History::Ink { .. }
@@ -1153,6 +1209,8 @@ impl CanvasEditor {
fresh.preferred_x = self.preferred_x;
fresh.ops = std::mem::replace(&mut self.ops, Ok(Vec::new()));
fresh.stored = self.stored.take();
+ fresh.default_font = std::mem::take(&mut self.default_font);
+ fresh.styles = std::mem::take(&mut self.styles);
*self = fresh;
Ok(true)
}
@@ -1230,6 +1288,12 @@ impl CanvasEditor {
// OneNote stores it, holds only those its paragraphs reference.
let mut referenced = BTreeSet::new();
for object in &objects {
+ let tags = match object {
+ PageObject::Image(image) => image.tags.as_slice(),
+ PageObject::Attachment(file) => file.tags.as_slice(),
+ _ => &[],
+ };
+ referenced.extend(tags.iter().filter_map(|tag| tag.definition));
let outlines = match object {
PageObject::Outline(outline) => std::slice::from_ref(outline),
PageObject::Title(title) => title.outlines.as_slice(),
@@ -1327,6 +1391,11 @@ impl CanvasEditor {
self.header.color
}
+ /// The page's notebook-management identity, which internal links name.
+ pub fn identity(&self) -> Option<[u8; 16]> {
+ self.header.identity
+ }
+
/// The page's rule lines (View, Rule Lines).
pub fn rule_lines(&self) -> Option {
self.header.rule_lines
@@ -1408,16 +1477,8 @@ impl CanvasEditor {
page::Content::Image(image) if !image.background => {
let x = image.layout.x.unwrap_or(0.0);
let y = image.layout.y.unwrap_or(0.0);
- Some((
- image.id,
- &image.layout,
- [
- x,
- y,
- x + image.layout.max_width?,
- y + image.layout.max_height?,
- ],
- ))
+ let [width, height] = crate::outline::image_size(image)?;
+ Some((image.id, &image.layout, [x, y, x + width, y + height]))
}
page::Content::ReadOnly(object)
if !matches!(object.source, onestore::page::PageObject::Title(_)) =>
@@ -1508,6 +1569,8 @@ impl CanvasEditor {
recording: None,
ink_drag: None,
provisional: BTreeMap::new(),
+ default_font: DefaultFont::default(),
+ styles: BTreeMap::new(),
})
}
@@ -1709,7 +1772,7 @@ impl CanvasEditor {
position: [f32; 2],
width: f32,
) -> Result<(), EditorError> {
- let document = TextDocument::new(vec![Paragraph::new(String::new(), Default::default())])?;
+ let document = TextDocument::from_nodes(vec![self.body_paragraph()?])?;
let outline = TextOutline::new(engine, document, width, position)?;
self.finish_composition();
self.active = Focus::Caret {
@@ -1736,6 +1799,34 @@ impl CanvasEditor {
})
}
+ /// A new outline's first paragraph as OneNote 2010 starts one: in the page's `p` quick
+ /// style of the Default font, which a page whose `p` is in another font gains beside it.
+ fn body_paragraph(&mut self) -> Result {
+ let definition = match self.styles.get("p") {
+ Some(themed) => themed.clone(),
+ None => self.default_font.body_style(),
+ };
+ let known = self.definitions.iter().find(|(_, known)| {
+ matches!(&known.kind, Kind::Style { name: Some(name), .. } if name == "p")
+ && known.format.font == definition.format.font
+ && known.format.font_size == definition.format.font_size
+ });
+ let style = match known {
+ Some((id, _)) => *id,
+ None => {
+ let id = onestore::page::text::new_id()?;
+ self.definitions.insert(id, definition.clone());
+ id
+ }
+ };
+ let mut node = crate::document::node(
+ Paragraph::new(String::new(), definition.format),
+ onestore::document::Format::default(),
+ )?;
+ node.style = Some(style);
+ Ok(node)
+ }
+
/// An empty paragraph in `base`'s style, whose runs keep `base`'s language as OneNote's do.
fn blank_paragraph(&self, base: &PageParagraph) -> Result {
let mut format = self.style_format(base.style)?;
@@ -1844,10 +1935,7 @@ impl CanvasEditor {
position: [f32; 2],
width: f32,
) -> Result {
- let document = TextDocument::new(vec![Paragraph::new(
- String::new(),
- onestore::document::Format::default(),
- )])?;
+ let document = TextDocument::from_nodes(vec![self.body_paragraph()?])?;
let outline = TextOutline::new(engine, document, width, position)?;
let id = outline.id;
self.finish_composition();
@@ -1935,7 +2023,7 @@ impl CanvasEditor {
let layout = &image.layout;
return Some((
[layout.x.unwrap_or(0.0), layout.y.unwrap_or(0.0)],
- [layout.max_width?, layout.max_height?],
+ crate::outline::image_size(image)?,
));
}
let (outline, ..) = self.outline_picture(id)?;
@@ -1963,6 +2051,18 @@ impl CanvasEditor {
})
}
+ /// The address picture `id` links to, in an outline or on the page.
+ pub fn picture_link(&self, id: ExGuid) -> Option<&str> {
+ let image = match self.image(id) {
+ Some(image) => image,
+ None => match &self.outline_picture(id)?.3.content {
+ ParagraphContent::Image(image) => image,
+ _ => return None,
+ },
+ };
+ image.link.as_deref()
+ }
+
/// The file a paragraph holds or the page shows, which the host opens and saves.
pub fn attachment(&self, id: ExGuid) -> Option<&onestore::page::Attachment> {
if let Some((file, _)) = self.page_file(id) {
@@ -2024,6 +2124,36 @@ impl CanvasEditor {
})
}
+ fn object_tags_mut(&mut self, id: ExGuid) -> Option<&mut Vec> {
+ self.objects.iter_mut().find_map(|object| match object {
+ page::Content::Image(image) if image.id == id => Some(&mut image.tags),
+ page::Content::File { source, .. } if source.id == id => Some(&mut source.tags),
+ _ => None,
+ })
+ }
+
+ /// Gives picture or file `id` on the page `tags`, returning those it had.
+ fn swap_object_tags(
+ &mut self,
+ id: ExGuid,
+ tags: Vec,
+ ) -> Vec {
+ let previous = std::mem::replace(self.object_tags_mut(id).unwrap(), tags);
+ let tags = self.object_tags_mut(id).unwrap().clone();
+ self.record(Ok(ops::tags(id, tags, &self.definitions)));
+ previous
+ }
+
+ /// Gives picture or file `id` on the page `tags`, as one undo step.
+ pub(super) fn set_object_tags(&mut self, id: ExGuid, tags: Vec) {
+ let previous = self.swap_object_tags(id, tags);
+ self.undo.push(History::Tags {
+ object: id,
+ tags: previous,
+ });
+ self.redo.clear();
+ }
+
fn image_mut(&mut self, id: ExGuid) -> Option<&mut onestore::page::Image> {
self.objects.iter_mut().find_map(|object| match object {
page::Content::Image(image) if image.id == id && !image.background => Some(image),
@@ -2079,7 +2209,7 @@ impl CanvasEditor {
let image = self.image(id).ok_or(EditError::InvalidRange)?;
let mut layout = image.layout.clone();
[layout.x, layout.y] = origin.map(Some);
- if [layout.max_width, layout.max_height] != size.map(Some) {
+ if crate::outline::image_size(image) != Some(size) {
[layout.max_width, layout.max_height] = size.map(Some);
layout.width_set_by_user = Some(true);
}
@@ -2180,12 +2310,39 @@ impl CanvasEditor {
/// Puts `image` at the caret as OneNote 2010 pastes and inserts a picture: in an outline
/// as [`Self::insert_attachment`] puts a file, and on blank page at the caret with the
- /// caret moving to the grid row below it, where typing starts a new outline.
+ /// caret moving to the grid row below it, where typing starts a new outline. From the
+ /// title it goes at the end of an outline where the body starts, in a new one there on a
+ /// page without body content, and otherwise on the page two grid rows below the content
+ /// (lab, 2026-09-30).
pub fn insert_picture(
&mut self,
engine: &mut TextEngine,
mut image: onestore::page::Image,
) -> Result<(), EditorError> {
+ let margin = self.margin_origin();
+ if self.active_outline().title {
+ let start = self.body_start_of(self.active_outline());
+ let first = self
+ .outlines
+ .iter()
+ .find(|outline| !outline.title && outline.origin() == start)
+ .map(|outline| outline.id);
+ if let Some(id) = first {
+ self.focus_outline(id)?;
+ self.move_selection(engine, Movement::DocumentEnd, false)?;
+ return self.insert_in_flow(engine, ParagraphContent::Image(image));
+ }
+ let Some(bottom) = self.body_bottom() else {
+ self.place_caret(engine, start, DEFAULT_OUTLINE_WIDTH)?;
+ return self.insert_in_flow(engine, ParagraphContent::Image(image));
+ };
+ let row = ((bottom - margin[1]) / 18.0).floor() + 2.0;
+ self.place_caret(
+ engine,
+ [start[0], margin[1] + row * 18.0],
+ DEFAULT_OUTLINE_WIDTH,
+ )?;
+ }
let Focus::Caret { outline, .. } = &self.active else {
return self.insert_in_flow(engine, ParagraphContent::Image(image));
};
@@ -2198,9 +2355,123 @@ impl CanvasEditor {
self.add_picture(index, image);
self.undo.push(History::Picture { index, image: None });
self.redo.clear();
- let margin = self.margin_origin()[1];
- let row = ((y + height - margin) / 18.0).floor() + 1.0;
- self.place_caret(engine, [x, margin + row * 18.0], DEFAULT_OUTLINE_WIDTH)
+ let row = ((y + height - margin[1]) / 18.0).floor() + 1.0;
+ self.place_caret(engine, [x, margin[1] + row * 18.0], DEFAULT_OUTLINE_WIDTH)
+ }
+
+ /// Pastes text in `language` and pictures in order, as one undo step, as OneNote 2010
+ /// pastes a web page's text with its pictures; the places of the awaited ones, in order.
+ pub fn paste_pieces(
+ &mut self,
+ engine: &mut TextEngine,
+ pieces: Vec,
+ language: u32,
+ ) -> Result, EditorError> {
+ let depth = self.undo.len();
+ let mut result = Ok(());
+ let mut awaited = Vec::new();
+ for piece in pieces {
+ result = match piece {
+ Piece::Text(text) => self.paste(engine, &text, language),
+ Piece::Picture(bytes, size) => picture(bytes, size)
+ .map_err(EditorError::from)
+ .and_then(|image| self.insert_picture(engine, image)),
+ Piece::Awaited => self.enter(engine, false).and_then(|()| {
+ let caret = self.selection().positions[1];
+ let outline = self.active_outline();
+ let (.., node) = outline
+ .document
+ .leaf(caret.paragraph)
+ .ok_or(EditError::InvalidRange)?;
+ awaited.push(Awaited {
+ outline: outline.id,
+ before: node.id,
+ });
+ Ok(())
+ }),
+ };
+ if result.is_err() {
+ break;
+ }
+ }
+ let entries = self.undo.split_off(depth);
+ if entries.len() > 1 {
+ self.undo.push(History::Group {
+ entries,
+ page: false,
+ });
+ } else {
+ self.undo.extend(entries);
+ }
+ result.map(|()| awaited)
+ }
+
+ /// Puts an awaited picture in its place as an undo step of its own, keeping the focused
+ /// outline's selection; false where the place has gone.
+ pub fn insert_awaited(
+ &mut self,
+ engine: &mut TextEngine,
+ at: Awaited,
+ image: onestore::page::Image,
+ ) -> Result {
+ let Some(outline) = self.outlines.iter().find(|outline| outline.id == at.outline) else {
+ return Ok(false);
+ };
+ let Some((container, index, node)) = descendants(outline.document.nodes(), None)
+ .find(|(.., node)| node.id == at.before)
+ .map(|(container, index, node)| (container, index, node.clone()))
+ else {
+ return Ok(false);
+ };
+ let picture = PageParagraph {
+ id: onestore::page::text::new_id()?,
+ content: ParagraphContent::Image(image),
+ style: None,
+ lists: Vec::new(),
+ tags: Vec::new(),
+ collapsed: false,
+ ..node
+ };
+ // A caret on blank page is let go; picture paragraphs take no text positions.
+ let focused = match self.active {
+ Focus::Outline(index) => Some((self.outlines[index].id, self.selection())),
+ _ => None,
+ };
+ self.focus_outline(at.outline)?;
+ let selection = self.selection();
+ self.commit(
+ engine,
+ DocumentEdit {
+ columns: BTreeMap::new(),
+ container,
+ range: index..index,
+ replacement: vec![picture],
+ },
+ selection,
+ )?;
+ if let Some((id, selection)) = focused {
+ self.focus_outline(id)?;
+ self.select(selection)?;
+ }
+ Ok(true)
+ }
+
+ /// The lowest edge of the page's body content, in page points; none on an empty page.
+ fn body_bottom(&self) -> Option {
+ let outlines = self
+ .outlines
+ .iter()
+ .filter(|outline| !outline.title)
+ .map(|outline| outline.bounds().y1 as f32);
+ let objects = self
+ .objects
+ .iter()
+ .filter(|object| !object.picture().is_some_and(|image| image.background))
+ .filter_map(|object| {
+ let (_, layout) = object.layout()?;
+ Some(layout.y? + layout.max_height?)
+ });
+ outlines.chain(objects).reduce(f32::max)
}
/// Attaches `file` at the caret as OneNote 2010 does (`corpus/attachment-insert`): the
@@ -2316,19 +2587,7 @@ impl CanvasEditor {
})
.ok_or(EditError::InvalidRange)?;
self.finish_composition();
- let mut image = self.remove_picture(index);
- // A printout page's XPS package cannot be written back, so undoing its deletion
- // restores the picture OneNote showed of it.
- if image.printout.is_some()
- && image
- .bytes
- .as_deref()
- .is_some_and(|bytes| bytes.starts_with(b"PK\x03\x04"))
- && let Some(shown) = image.display.take()
- {
- image.bytes = Some(shown);
- image.printout = None;
- }
+ let image = self.remove_picture(index);
self.undo.push(History::Picture {
index,
image: Some(Box::new(image)),
@@ -3401,8 +3660,13 @@ impl CanvasEditor {
tail.tags = std::mem::take(&mut head.tags);
tail.text_mut().unwrap().tags = std::mem::take(&mut head.text_mut().unwrap().tags);
}
- let style = |id: Option| match &self.definitions.get(&id?)?.kind {
- Kind::Style { name } => name.as_deref(),
+ // Enter at a paragraph's end gives the new one its style's NextStyle, as OneNote 2010
+ // follows a heading with Normal.
+ let following = match head.style.and_then(|id| self.definitions.get(&id)) {
+ Some(Definition {
+ kind: Kind::Style { name, next },
+ ..
+ }) => next.clone().filter(|next| Some(next) != name.as_ref()),
_ => None,
};
if empty(head) && empty(tail) {
@@ -3411,16 +3675,9 @@ impl CanvasEditor {
tail.lists.clear();
}
} else if empty(tail)
- && matches!(
- style(head.style),
- Some("h1" | "h2" | "h3" | "h4" | "h5" | "h6")
- )
+ && let Some(following) = following
{
- tail.style = self
- .definitions
- .keys()
- .copied()
- .find(|id| style(Some(*id)) == Some("p"));
+ tail.style = self.style_named(&following)?;
// The heading's run formatting carries over under the body style (`c9-h1-*`).
let run = format.over(&self.style_format(head.style)?);
tail.text_mut().unwrap().text =
@@ -3730,6 +3987,26 @@ impl CanvasEditor {
!self.redo.is_empty()
}
+ /// How many steps Undo and Redo hold.
+ pub fn history_depth(&self) -> [usize; 2] {
+ [self.undo.len(), self.redo.len()]
+ }
+
+ /// Takes up `parked`, the editor this page had when it was last left, history and all,
+ /// in place of this one: what storage changed since reaches it as `refresh` would.
+ pub fn resume(
+ &mut self,
+ mut parked: CanvasEditor,
+ engine: &mut TextEngine,
+ ) -> Result<(), EditorError> {
+ if let Some((page, _)) = &self.stored {
+ parked.refresh(page.clone(), engine)?;
+ parked.default_font = std::mem::take(&mut self.default_font);
+ *self = parked;
+ }
+ Ok(())
+ }
+
pub fn undo(&mut self, engine: &mut TextEngine) -> Result {
if self.composition.is_some() {
return self.cancel_composition(engine);
@@ -3842,6 +4119,7 @@ impl CanvasEditor {
.iter()
.all(|id| self.outlines.iter().any(|item| item.id == **id)),
History::Image { image, .. } => self.image(*image).is_some(),
+ History::Tags { object, .. } => self.object_tags_mut(*object).is_some(),
History::Picture {
index,
image: Some(_),
@@ -4073,6 +4351,10 @@ impl CanvasEditor {
layout: previous,
}
}
+ History::Tags { object, tags } => History::Tags {
+ object,
+ tags: self.swap_object_tags(object, tags),
+ },
History::Image { image, layout } => {
let previous =
std::mem::replace(&mut self.image_mut(image).unwrap().layout, layout);
@@ -4262,6 +4544,7 @@ impl CanvasEditor {
.replace(range.clone(), replacement)?;
self.own_lists(&mut edit)?;
self.link_notes(&mut edit)?;
+ self.fit_columns(engine, &mut edit)?;
let inverse = self.apply(
engine,
edit,
@@ -4361,6 +4644,7 @@ impl CanvasEditor {
) -> Result<(), EditorError> {
self.own_lists(&mut edit)?;
self.link_notes(&mut edit)?;
+ self.fit_columns(engine, &mut edit)?;
let inverse = self.apply(engine, edit, selection, true, None)?;
self.record_change(inverse);
Ok(())
@@ -6152,6 +6436,9 @@ mod tests {
alt: None,
background,
printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
});
let (id, background_id) = (picture.id, background.id);
let mut editor = CanvasEditor::from_page(
@@ -6260,6 +6547,9 @@ mod tests {
alt: None,
background,
printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
};
let [old, picture] = [image(true, b"old art"), image(false, b"picture")];
let mut editor = CanvasEditor::from_page(
@@ -6349,6 +6639,9 @@ mod tests {
alt: None,
background: false,
printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
};
let id = picture.id;
let objects = vec![
@@ -6414,6 +6707,9 @@ mod tests {
alt: None,
background: false,
printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
};
picture.content = ParagraphContent::Image(image.clone());
source.paragraphs.insert(1, picture);
@@ -6562,6 +6858,7 @@ mod tests {
bytes: Some(std::sync::Arc::from(b"payload".as_slice())),
preview: None,
recording: None,
+ tags: Vec::new(),
};
let at = |paragraph, offset| Selection {
positions: [TextPosition { paragraph, offset }; 2],
@@ -6651,6 +6948,9 @@ mod tests {
alt: None,
background: false,
printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
});
let (mixed_id, picture_id) = (mixed.id, picture_only.id);
let editor = CanvasEditor::from_page(
@@ -6736,6 +7036,9 @@ mod tests {
alt: None,
background,
printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
});
let image_ids = images.each_ref().map(|image| image.id);
let image_bytes = images
@@ -6990,6 +7293,7 @@ mod tests {
Definition {
kind: onestore::document::Kind::Style {
name: Some("p".into()),
+ next: None,
},
format: base.clone(),
},
@@ -7057,6 +7361,7 @@ mod tests {
Definition {
kind: onestore::document::Kind::Style {
name: Some("p".into()),
+ next: None,
},
format: base.clone(),
},
@@ -7258,6 +7563,47 @@ mod tests {
assert_eq!(editor.outlines[0].document, source);
}
+ /// New text takes the page's `p` quick style in the Default font, and a page whose `p`
+ /// is in another font gains a second `p` beside it, as OneNote 2010 does
+ /// (`corpus/default-font`).
+ #[test]
+ fn new_outlines_take_a_body_style_in_the_default_font() {
+ let mut engine = TextEngine::default();
+ let source =
+ TextDocument::new(vec![Paragraph::new("body".into(), Format::default())]).unwrap();
+ let mut editor = CanvasEditor::new(&mut engine, source, 240.0).unwrap();
+ let style = |editor: &CanvasEditor, outline: usize| {
+ let node = &editor.outlines[outline].document.nodes()[0];
+ let definition = &editor.definitions[&node.style.unwrap()];
+ assert_eq!(
+ node.text().unwrap().text.spans()[0].format,
+ definition.format
+ );
+ (node.style.unwrap(), definition.clone())
+ };
+ editor.default_font = DefaultFont {
+ face: "Georgia".into(),
+ size: 14.0,
+ color: None,
+ };
+ for (typed, at) in [("one", 300.0), ("two", 400.0)] {
+ editor.place_caret(&mut engine, [300.0, at], 240.0).unwrap();
+ editor.insert(&mut engine, typed).unwrap();
+ }
+ let (first, georgia) = style(&editor, 1);
+ assert_eq!(style(&editor, 2).0, first, "one p per font");
+ assert_eq!(georgia, editor.default_font.body_style());
+ editor.default_font = DefaultFont::default();
+ editor
+ .place_caret(&mut engine, [300.0, 500.0], 240.0)
+ .unwrap();
+ editor.insert(&mut engine, "three").unwrap();
+ let (third, calibri) = style(&editor, 3);
+ assert_ne!(third, first);
+ assert_eq!(calibri.format.font.as_deref(), Some("Calibri"));
+ assert_eq!(calibri.format.font_size, Some(11.0));
+ }
+
#[test]
fn deleting_through_provisional_paragraphs_removes_the_outline_and_restores_draft_on_undo() {
let mut engine = TextEngine::default();
@@ -9314,6 +9660,9 @@ mod tests {
alt: None,
background: false,
printout: None,
+ tags: Vec::new(),
+ link: None,
+ text: None,
});
if objects {
let selection = [TextPosition {
diff --git a/crates/canvas/src/editor/evidence.rs b/crates/canvas/src/editor/evidence.rs
index 21e92c05ac22102c3a301dcbbde4da240392e514..20a3129a03e7d652750efd6799a57d85fffcf889 100644
--- a/crates/canvas/src/editor/evidence.rs
+++ b/crates/canvas/src/editor/evidence.rs
@@ -47,6 +47,7 @@ fn open(engine: &mut TextEngine, summary: &str) -> CanvasEditor {
Definition {
kind: Kind::Style {
name: Some(name.into()),
+ next: (name == "h1").then(|| "p".into()),
},
format: calibri(if name == "p" { 11.0 } else { 16.0 }, name != "p"),
},
@@ -182,7 +183,7 @@ fn render(editor: &CanvasEditor) -> String {
let style = node
.style
.map(|style| match &editor.definitions[&style].kind {
- Kind::Style { name } => {
+ Kind::Style { name, .. } => {
format!("qs{}", usize::from(name.as_deref() == Some("h1")))
}
kind => panic!("{kind:?}"),
diff --git a/crates/canvas/src/editor/format.rs b/crates/canvas/src/editor/format.rs
index 2daf7dfa2e1dabd7518192800d28b7560f76a9b9..af9e92437a5a14f3ae0878c601b2dbba243150cc 100644
--- a/crates/canvas/src/editor/format.rs
+++ b/crates/canvas/src/editor/format.rs
@@ -43,6 +43,10 @@ pub enum Formatting {
/// Format Painter: gives the selection the character formatting and alignment of text
/// picked up with [`CanvasEditor::painted_format`], as OneNote's does.
Paint(Format),
+ /// Gives the selected paragraphs a paragraph style, as OneNote 2010's Styles gallery
+ /// does: the page's style of that definition, made where it has none, and their text
+ /// loses its character formatting but links, fields and language.
+ Style(Definition),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -164,6 +168,57 @@ impl ListStyle {
}
}
+/// OneNote's Default font (Options > General): what new outlines' text is set in, as the
+/// page's `p` quick style, and the face and colour new titles take.
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(default)]
+pub struct DefaultFont {
+ pub face: String,
+ pub size: f32,
+ /// COLORREF; none is Automatic.
+ pub color: Option,
+}
+
+impl Default for DefaultFont {
+ /// OneNote 2010's: Calibri 11, Automatic.
+ fn default() -> Self {
+ Self {
+ face: "Calibri".into(),
+ size: 11.0,
+ color: None,
+ }
+ }
+}
+
+impl DefaultFont {
+ /// The `p` quick style OneNote 2010 stores for new text in this font
+ /// (`corpus/default-font`): plain, in its colour, no highlight or paragraph spacing.
+ pub(crate) fn body_style(&self) -> Definition {
+ Definition {
+ kind: Kind::Style {
+ name: Some("p".into()),
+ next: None,
+ },
+ format: Format {
+ bold: Some(false),
+ italic: Some(false),
+ underline: Some(false),
+ strike: Some(false),
+ superscript: Some(false),
+ subscript: Some(false),
+ font: Some(self.face.clone()),
+ font_size: Some(self.size),
+ color: Some(self.color.unwrap_or(0xff00_0000)),
+ highlight: Some(0xff00_0000),
+ space_before: Some(0.0),
+ space_after: Some(0.0),
+ line_spacing: Some(0.0),
+ ..Format::default()
+ },
+ }
+ }
+}
+
/// A tag of the user's list as the Customize Tags dialog edits it: what its definition
/// stores besides its place in the list, which the page stores as its action type.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
@@ -293,6 +348,8 @@ pub struct FormatState {
pub list: Option,
/// Tags every selected paragraph has, with their action types.
pub tags: Vec<(NoteTag, u16)>,
+ /// The stored name of the paragraph style every selected paragraph shares.
+ pub style: Option,
}
impl Toggle {
@@ -477,6 +534,35 @@ fn common(mut values: impl Iterator- >) -> Option Format {
+ let mut format = format.clone();
+ macro_rules! follow {
+ ($($field:ident),*) => {$(
+ if format.$field.is_none() || old.$field.is_some() && format.$field == old.$field {
+ format.$field = new.$field.clone();
+ }
+ )*};
+ }
+ follow!(
+ bold,
+ italic,
+ underline,
+ strike,
+ superscript,
+ subscript,
+ font,
+ font_size,
+ color,
+ highlight,
+ space_before,
+ space_after,
+ line_spacing
+ );
+ format
+}
+
/// Seconds since 1980, as note tags date themselves.
fn time32() -> Option {
let now = std::time::SystemTime::now()
@@ -629,6 +715,12 @@ impl CanvasEditor {
shared.sort_by_key(|(_, action_type)| *action_type);
shared
}),
+ style: common(paragraphs.iter().map(|node| {
+ match &self.definitions.get(&node.style?)?.kind {
+ Kind::Style { name, .. } => name.clone(),
+ _ => None,
+ }
+ })),
})
}
@@ -765,36 +857,8 @@ impl CanvasEditor {
let ParagraphContent::Text(text) = &mut node.content else {
unreachable!()
};
- // An element holds one tag of each action type.
- for tags in [&mut node.tags, &mut text.tags] {
- tags.retain(|tag| match self.tag_kind(tag) {
- Some(kind) if remove => *kind != definition.kind,
- Some(Kind::TagDefinition {
- action_type: other, ..
- }) => *other != Some(*action_type),
- _ => true,
- });
- }
- if !remove {
- let checkable = crate::outline::checkable(tag.shape);
- // Stored newest first.
- text.tags.insert(
- 0,
- Tag {
- definition: Some(id),
- action_type: None,
- shape: None,
- property_status: None,
- status: u16::from(!checkable),
- created,
- completed: if checkable { Some(0) } else { created },
- start: None,
- due: None,
- task_id: None,
- extra_set: 0,
- },
- );
- }
+ let added = (!remove).then_some((id, tag.shape, created));
+ self.retag([&mut node.tags, &mut text.tags], &definition.kind, added);
});
}
Formatting::RemoveTags => leaves_mut(&mut replacement, &mut |node| {
@@ -804,6 +868,39 @@ impl CanvasEditor {
}
}),
Formatting::Check => self.check(&mut replacement, ranges, ends),
+ Formatting::Style(_) if title => return Ok(()),
+ Formatting::Style(definition) => {
+ let olds = bases(&replacement)?;
+ let style = self.define_style(definition)?;
+ leaves_mut(&mut replacement, &mut |node| {
+ if ranges.remove(&node.id).is_some() {
+ node.style = Some(style);
+ let old = &olds[&node.id];
+ let text = &mut node.text_mut().unwrap().text;
+ *text = restyle(text, 0..text.text().len(), |format| {
+ // An equation keeps its own formatting; what it took from the
+ // old style it takes from the new.
+ if [format.math, format.embedded_object].contains(&Some(true)) {
+ *format = followed(format, old, &definition.format);
+ return;
+ }
+ *format = Format {
+ hidden: format.hidden,
+ hyperlink: format.hyperlink,
+ hyperlink_label: format.hyperlink_label,
+ math: format.math,
+ embedded_object: format.embedded_object,
+ language: format.language,
+ alignment: format.alignment,
+ rtl: format.rtl,
+ list_spacing: format.list_spacing,
+ math_object: format.math_object.clone(),
+ ..definition.format.clone()
+ };
+ });
+ }
+ });
+ }
Formatting::Toggle(_)
| Formatting::Font(_)
| Formatting::FontSize(_)
@@ -906,6 +1003,43 @@ impl CanvasEditor {
}
}
+impl CanvasEditor {
+ /// The page's paragraph style holding `definition`, made where it has none.
+ pub(super) fn define_style(&mut self, definition: &Definition) -> Result {
+ if let Some((id, _)) = self
+ .definitions
+ .iter()
+ .find(|(_, kept)| *kept == definition)
+ {
+ return Ok(*id);
+ }
+ let id = new_id()?;
+ self.definitions.insert(id, definition.clone());
+ Ok(id)
+ }
+
+ /// A paragraph style named `name`: the page's in its theme's formatting, else the page's
+ /// first of that name, else the theme's made on the page; Normal falls back to the
+ /// Default font's. None where nothing names it.
+ pub(super) fn style_named(&mut self, name: &str) -> Result