onenote-converter 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. onenote_converter-0.1.2/CHANGELOG.md +73 -0
  2. onenote_converter-0.1.2/Cargo.lock +203 -0
  3. onenote_converter-0.1.2/Cargo.toml +38 -0
  4. onenote_converter-0.1.2/LICENSE +21 -0
  5. onenote_converter-0.1.2/LICENSE.python +21 -0
  6. onenote_converter-0.1.2/PKG-INFO +136 -0
  7. onenote_converter-0.1.2/README.md +265 -0
  8. onenote_converter-0.1.2/RELEASING.md +113 -0
  9. onenote_converter-0.1.2/pyproject.toml +41 -0
  10. onenote_converter-0.1.2/python/Cargo.toml +27 -0
  11. onenote_converter-0.1.2/python/LICENSE.python +21 -0
  12. onenote_converter-0.1.2/python/README.md +109 -0
  13. onenote_converter-0.1.2/python/requirements-dev.txt +4 -0
  14. onenote_converter-0.1.2/python/src/lib.rs +621 -0
  15. onenote_converter-0.1.2/python/tests/data/README.md +7 -0
  16. onenote_converter-0.1.2/python/tests/data/notebook/Group Section 1/Group Section 1-a/Group Section 1-a.onetoc2 +0 -0
  17. onenote_converter-0.1.2/python/tests/data/notebook/Group Section 1/Group Section 1-a/Subsection 3.one +0 -0
  18. onenote_converter-0.1.2/python/tests/data/notebook/Group Section 1/Group Section 1.onetoc2 +0 -0
  19. onenote_converter-0.1.2/python/tests/data/notebook/Group Section 1/Subsection 1.one +0 -0
  20. onenote_converter-0.1.2/python/tests/data/notebook/Group Section 1/Subsection 2.one +0 -0
  21. onenote_converter-0.1.2/python/tests/data/notebook/Nova Se/303/247/303/243o 1.one +0 -0
  22. onenote_converter-0.1.2/python/tests/data/notebook/Open Notebook.onetoc2 +0 -0
  23. onenote_converter-0.1.2/python/tests/test_onenote.py +215 -0
  24. onenote_converter-0.1.2/scripts/check.sh +76 -0
  25. onenote_converter-0.1.2/src/cab.rs +365 -0
  26. onenote_converter-0.1.2/src/doc.rs +1254 -0
  27. onenote_converter-0.1.2/src/filenode.rs +331 -0
  28. onenote_converter-0.1.2/src/fsshttpb.rs +909 -0
  29. onenote_converter-0.1.2/src/guid.rs +103 -0
  30. onenote_converter-0.1.2/src/header.rs +226 -0
  31. onenote_converter-0.1.2/src/lib.rs +55 -0
  32. onenote_converter-0.1.2/src/main.rs +1136 -0
  33. onenote_converter-0.1.2/src/markdown.rs +317 -0
  34. onenote_converter-0.1.2/src/notebook.rs +448 -0
  35. onenote_converter-0.1.2/src/output.rs +390 -0
  36. onenote_converter-0.1.2/src/packaging.rs +413 -0
  37. onenote_converter-0.1.2/src/prop.rs +850 -0
  38. onenote_converter-0.1.2/src/reader.rs +39 -0
  39. onenote_converter-0.1.2/src/store.rs +866 -0
  40. onenote_converter-0.1.2/tests/fixtures/README.md +23 -0
  41. onenote_converter-0.1.2/tests/fixtures/test-tika-3970-dupetext.one +0 -0
  42. onenote_converter-0.1.2/tests/fixtures/testOneNote2016.one +0 -0
  43. onenote_converter-0.1.2/tests/fuzz.rs +155 -0
  44. onenote_converter-0.1.2/tests/onestore.rs +252 -0
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres
5
+ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.2] - 2026-10-27
10
+
11
+ ### Fixed
12
+
13
+ - Wheels now carry the license text (`dist-info/licenses/LICENSE.python`), as the
14
+ MIT terms require; `scripts/check.sh` and CI fail if the copy drifts from the
15
+ repository `LICENSE`.
16
+
17
+ ## [0.1.1] - 2026-10-27
18
+
19
+ Packaging and verification release; no parser behaviour changed.
20
+
21
+ ### Fixed
22
+
23
+ - CI could not build the Python extension: `maturin develop` needs a virtualenv,
24
+ so the workflow now builds a wheel and installs it (the same commands
25
+ `scripts/check.sh` runs).
26
+ - The sdist no longer carries `.gitignore` or `PLAN.md`.
27
+
28
+ ### Changed
29
+
30
+ - Python floor is `>= 3.9` everywhere (the abi3 feature matched it).
31
+ - Runner labels are pinned (`ubuntu-24.04`, `macos-15`, `macos-15-intel`,
32
+ `windows-2025`) so label migrations cannot change the matrix.
33
+ - `scripts/check.sh` reproduces the CI gate, including creating a throwaway
34
+ virtualenv on a bare checkout and installing the built sdist into a clean one.
35
+
36
+ ## [0.1.0] - 2026-10-26
37
+
38
+ First release: a CLI, a Rust library and Python bindings over one parser.
39
+
40
+ ### Added
41
+
42
+ - **Sections** (`.one`): reads `[MS-ONESTORE]` revision stores — header, file node
43
+ lists, property sets, object spaces, revisions, object groups, file data.
44
+ - **Packages** (`.onepkg`): reads Microsoft Cabinets with stored and MSZIP
45
+ folders; Quantum and LZX are rejected with a message.
46
+ - **Notebooks** (`.onetoc2`): the table of contents is honoured exactly as given,
47
+ section groups become subfolders with their own `index.md`, and entries whose
48
+ file or folder is missing are reported instead of skipped.
49
+ - **Markdown output**: page headings, paragraphs, numbered and bulleted lists
50
+ with nesting, tables, pictures (extracted with stable deduplicated names),
51
+ attachments, ink markers, note tags, and inline run formatting (bold, italic,
52
+ strike, underline, colour, highlight, hyperlinks).
53
+ - **YAML front matter** per page: title, creation and modification dates,
54
+ authors, page level.
55
+ - **Diagnostics**: `header` and `inspect` commands (object spaces, declarations,
56
+ property sets, blob ranges, text search, hashed chunk list, transaction log).
57
+ - **Packaged stores** (`[MS-FSSHTTPB]`): data elements, cells, object data and
58
+ the storage index are translated into the same object model as plain stores.
59
+ - **Python bindings** (`onenote`): `read_section`, `read_notebook`,
60
+ `read_package` with `Section`/`Page`/`Image`/`Notebook`/`Package`, abi3 wheels
61
+ for CPython 3.9+.
62
+ - **Machine-readable runs**: `--report` writes a JSON summary, `--strict` turns
63
+ any warning into a non-zero exit status.
64
+
65
+ ### Notes
66
+
67
+ - Encrypted sections are detected and reported; their content is not decrypted.
68
+ - Format decisions and the measurements behind them are recorded in `PLAN.md`.
69
+
70
+ [Unreleased]: https://github.com/wsmontes/one-note-converter/compare/v0.1.2...HEAD
71
+ [0.1.2]: https://github.com/wsmontes/one-note-converter/releases/tag/v0.1.2
72
+ [0.1.1]: https://github.com/wsmontes/one-note-converter/releases/tag/v0.1.1
73
+ [0.1.0]: https://github.com/wsmontes/one-note-converter/releases/tag/v0.1.0
@@ -0,0 +1,203 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 3
4
+
5
+ [[package]]
6
+ name = "autocfg"
7
+ version = "1.5.1"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
10
+
11
+ [[package]]
12
+ name = "cfg-if"
13
+ version = "1.0.5"
14
+ source = "registry+https://github.com/rust-lang/crates.io-index"
15
+ checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
16
+
17
+ [[package]]
18
+ name = "flate2"
19
+ version = "1.1.10"
20
+ source = "registry+https://github.com/rust-lang/crates.io-index"
21
+ checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
22
+ dependencies = [
23
+ "zlib-rs",
24
+ ]
25
+
26
+ [[package]]
27
+ name = "heck"
28
+ version = "0.5.0"
29
+ source = "registry+https://github.com/rust-lang/crates.io-index"
30
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
31
+
32
+ [[package]]
33
+ name = "indoc"
34
+ version = "2.0.8"
35
+ source = "registry+https://github.com/rust-lang/crates.io-index"
36
+ checksum = "a37b2691796cffeb8a8cd305ac66e65841559f147f4e63231d0eafa4db5384d1"
37
+ dependencies = [
38
+ "rustversion",
39
+ ]
40
+
41
+ [[package]]
42
+ name = "libc"
43
+ version = "0.2.191"
44
+ source = "registry+https://github.com/rust-lang/crates.io-index"
45
+ checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
46
+
47
+ [[package]]
48
+ name = "memoffset"
49
+ version = "0.9.1"
50
+ source = "registry+https://github.com/rust-lang/crates.io-index"
51
+ checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
52
+ dependencies = [
53
+ "autocfg",
54
+ ]
55
+
56
+ [[package]]
57
+ name = "once_cell"
58
+ version = "1.21.4"
59
+ source = "registry+https://github.com/rust-lang/crates.io-index"
60
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
61
+
62
+ [[package]]
63
+ name = "one-note-converter"
64
+ version = "0.1.2"
65
+ dependencies = [
66
+ "flate2",
67
+ ]
68
+
69
+ [[package]]
70
+ name = "onenote-py"
71
+ version = "0.1.2"
72
+ dependencies = [
73
+ "one-note-converter",
74
+ "pyo3",
75
+ ]
76
+
77
+ [[package]]
78
+ name = "portable-atomic"
79
+ version = "1.15.0"
80
+ source = "registry+https://github.com/rust-lang/crates.io-index"
81
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
82
+
83
+ [[package]]
84
+ name = "proc-macro2"
85
+ version = "1.0.107"
86
+ source = "registry+https://github.com/rust-lang/crates.io-index"
87
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
88
+ dependencies = [
89
+ "unicode-ident",
90
+ ]
91
+
92
+ [[package]]
93
+ name = "pyo3"
94
+ version = "0.23.5"
95
+ source = "registry+https://github.com/rust-lang/crates.io-index"
96
+ checksum = "7778bffd85cf38175ac1f545509665d0b9b92a198ca7941f131f85f7a4f9a872"
97
+ dependencies = [
98
+ "cfg-if",
99
+ "indoc",
100
+ "libc",
101
+ "memoffset",
102
+ "once_cell",
103
+ "portable-atomic",
104
+ "pyo3-build-config",
105
+ "pyo3-ffi",
106
+ "pyo3-macros",
107
+ "unindent",
108
+ ]
109
+
110
+ [[package]]
111
+ name = "pyo3-build-config"
112
+ version = "0.23.5"
113
+ source = "registry+https://github.com/rust-lang/crates.io-index"
114
+ checksum = "94f6cbe86ef3bf18998d9df6e0f3fc1050a8c5efa409bf712e661a4366e010fb"
115
+ dependencies = [
116
+ "once_cell",
117
+ "target-lexicon",
118
+ ]
119
+
120
+ [[package]]
121
+ name = "pyo3-ffi"
122
+ version = "0.23.5"
123
+ source = "registry+https://github.com/rust-lang/crates.io-index"
124
+ checksum = "e9f1b4c431c0bb1c8fb0a338709859eed0d030ff6daa34368d3b152a63dfdd8d"
125
+ dependencies = [
126
+ "libc",
127
+ "pyo3-build-config",
128
+ ]
129
+
130
+ [[package]]
131
+ name = "pyo3-macros"
132
+ version = "0.23.5"
133
+ source = "registry+https://github.com/rust-lang/crates.io-index"
134
+ checksum = "fbc2201328f63c4710f68abdf653c89d8dbc2858b88c5d88b0ff38a75288a9da"
135
+ dependencies = [
136
+ "proc-macro2",
137
+ "pyo3-macros-backend",
138
+ "quote",
139
+ "syn",
140
+ ]
141
+
142
+ [[package]]
143
+ name = "pyo3-macros-backend"
144
+ version = "0.23.5"
145
+ source = "registry+https://github.com/rust-lang/crates.io-index"
146
+ checksum = "fca6726ad0f3da9c9de093d6f116a93c1a38e417ed73bf138472cf4064f72028"
147
+ dependencies = [
148
+ "heck",
149
+ "proc-macro2",
150
+ "pyo3-build-config",
151
+ "quote",
152
+ "syn",
153
+ ]
154
+
155
+ [[package]]
156
+ name = "quote"
157
+ version = "1.0.47"
158
+ source = "registry+https://github.com/rust-lang/crates.io-index"
159
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
160
+ dependencies = [
161
+ "proc-macro2",
162
+ ]
163
+
164
+ [[package]]
165
+ name = "rustversion"
166
+ version = "1.0.23"
167
+ source = "registry+https://github.com/rust-lang/crates.io-index"
168
+ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
169
+
170
+ [[package]]
171
+ name = "syn"
172
+ version = "2.0.119"
173
+ source = "registry+https://github.com/rust-lang/crates.io-index"
174
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
175
+ dependencies = [
176
+ "proc-macro2",
177
+ "quote",
178
+ "unicode-ident",
179
+ ]
180
+
181
+ [[package]]
182
+ name = "target-lexicon"
183
+ version = "0.12.16"
184
+ source = "registry+https://github.com/rust-lang/crates.io-index"
185
+ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
186
+
187
+ [[package]]
188
+ name = "unicode-ident"
189
+ version = "1.0.27"
190
+ source = "registry+https://github.com/rust-lang/crates.io-index"
191
+ checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
192
+
193
+ [[package]]
194
+ name = "unindent"
195
+ version = "0.2.4"
196
+ source = "registry+https://github.com/rust-lang/crates.io-index"
197
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
198
+
199
+ [[package]]
200
+ name = "zlib-rs"
201
+ version = "0.6.8"
202
+ source = "registry+https://github.com/rust-lang/crates.io-index"
203
+ checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112"
@@ -0,0 +1,38 @@
1
+ [workspace]
2
+ members = [".", "python"]
3
+ resolver = "2"
4
+
5
+ [workspace.package]
6
+ version = "0.1.2"
7
+ edition = "2021"
8
+ rust-version = "1.82"
9
+ license = "MIT"
10
+ repository = "https://github.com/wsmontes/one-note-converter"
11
+ homepage = "https://github.com/wsmontes/one-note-converter"
12
+ authors = ["Wagner Montes <wmontes@gmail.com>"]
13
+
14
+ [package]
15
+ name = "one-note-converter"
16
+ description = "Convert Microsoft OneNote .one sections, .onetoc2 notebooks and .onepkg packages to Markdown"
17
+ version.workspace = true
18
+ edition.workspace = true
19
+ rust-version.workspace = true
20
+ license.workspace = true
21
+ repository.workspace = true
22
+ homepage.workspace = true
23
+ authors.workspace = true
24
+ documentation = "https://docs.rs/one-note-converter"
25
+ readme = "README.md"
26
+ keywords = ["onenote", "markdown", "ms-onestore", "converter", "notes"]
27
+ categories = ["parser-implementations", "command-line-utilities"]
28
+ exclude = ["python", ".github", "target"]
29
+
30
+ [[bin]]
31
+ name = "onenote2md"
32
+ path = "src/main.rs"
33
+
34
+ [dependencies]
35
+ flate2 = { version = "1", default-features = false, features = ["zlib-rs"] }
36
+
37
+ [package.metadata.docs.rs]
38
+ all-features = true
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wagner Montes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wagner Montes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.4
2
+ Name: onenote-converter
3
+ Version: 0.1.2
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Operating System :: OS Independent
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Programming Language :: Rust
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
13
+ License-File: LICENSE.python
14
+ Summary: Read Microsoft OneNote .one sections, .onetoc2 notebooks and .onepkg packages in Python
15
+ Keywords: onenote,markdown,ms-onestore,converter,notebook
16
+ Home-Page: https://github.com/wsmontes/one-note-converter
17
+ Author-email: Wagner Montes <wmontes@gmail.com>
18
+ License-Expression: MIT
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
21
+ Project-URL: Changelog, https://github.com/wsmontes/one-note-converter/blob/main/CHANGELOG.md
22
+ Project-URL: Documentation, https://github.com/wsmontes/one-note-converter/blob/main/python/README.md
23
+ Project-URL: Homepage, https://github.com/wsmontes/one-note-converter
24
+ Project-URL: Issues, https://github.com/wsmontes/one-note-converter/issues
25
+ Project-URL: Repository, https://github.com/wsmontes/one-note-converter
26
+
27
+ # onenote — Python bindings
28
+
29
+ `pip install onenote-converter` (the distribution is `onenote-converter`, the module is `onenote`).
30
+
31
+ Python API over the same Rust parser the `onenote2md` CLI uses. Nothing is
32
+ re-implemented here: a `.one` section, a `.onetoc2` notebook and a `.onepkg`
33
+ package are parsed by the crate in the parent directory.
34
+
35
+ ## Install
36
+
37
+ Requires Rust (stable) only when building.
38
+
39
+ ```bash
40
+ # development: build and install into the active virtualenv
41
+ python -m venv .venv && . .venv/bin/activate
42
+ pip install maturin
43
+ maturin develop --release # or: maturin build --release + pip install target/wheels/*.whl
44
+
45
+ # a wheel for distribution (abi3: one wheel for CPython ≥ 3.8)
46
+ maturin build --release # → target/wheels/onenote-<ver>-cp38-abi3-<platform>.whl
47
+ pip install target/wheels/onenote-*.whl
48
+ ```
49
+
50
+ ## API
51
+
52
+ ```python
53
+ import onenote
54
+
55
+ section = onenote.read_section("Section.one") # path or bytes
56
+ section.page_count # int
57
+ page = section.pages[0]
58
+ page.title, page.level, page.created, page.modified # ISO-8601 UTC strings
59
+ page.authors # list[str]
60
+ page.images # list[Image]
61
+
62
+ section.markdown(front_matter=True) # str, pictures as bare names
63
+ section.images() # list[Image] with .filename/.data
64
+
65
+ stats = section.write("out/Section.md", "out/img", front_matter=True)
66
+ stats.pages, stats.assets, stats.failures
67
+ ```
68
+
69
+ `Image.data` is `bytes`. Names are unique and deduplicated per section **and are
70
+ the same names `markdown()` links to**, so the in-memory flow is safe:
71
+
72
+ ```python
73
+ md = section.markdown()
74
+ for image in section.images():
75
+ Path(image.filename).write_bytes(image.data) # every delivered `![](name)` resolves
76
+ ```
77
+
78
+ `Page.images` uses the same names, filtered to the pictures that page references.
79
+ Links are percent-encoded; `Image.filename` is the raw name.
80
+
81
+ Some links have no payload in the file — OneNote web clippings and external
82
+ caches reference a picture without embedding it. Those are listed by
83
+ `section.missing_images()` (raw names), so callers can tell "delivered" from
84
+ "referenced only": `links ⊆ {i.filename for i in section.images()} ∪ set(section.missing_images())`.
85
+
86
+ ```python
87
+ notebook = onenote.read_notebook("Open Notebook.onetoc2")
88
+ notebook.name, notebook.toc
89
+ notebook.sections # [NotebookSection(name, path)]
90
+ notebook.groups # [Notebook] for section groups
91
+ notebook.errors # entries whose file/folder is missing
92
+ notebook.all_sections() # every section, depth first
93
+
94
+ stats = notebook.write("out/notebook", front_matter=True)
95
+ # out/notebook/<Section>.md + <Section>_assets/, one index.md per level
96
+ ```
97
+
98
+ The tree reflects what conversion does: a folder with its own `.onetoc2` is a
99
+ group even when the parent index does not list it, and `.onetoc2` dotfiles (the
100
+ OneNote Recycle Bin sentinel) are ignored.
101
+
102
+ ```python
103
+ package = onenote.read_package("Open Notebook.onepkg")
104
+ package.entries # file names inside the cabinet
105
+ package.sections() # parse every .one entry
106
+ package.extract_to("dir") # write the entries and return their paths
107
+ stats = package.write("out/package", front_matter=True)
108
+ ```
109
+
110
+ `Package.write` follows the CLI: when the cabinet carries a table of contents it
111
+ converts a notebook tree, otherwise each section on its own. MSZIP and stored
112
+ (no compression) folders are supported; Quantum/LZX raise `ValueError`.
113
+
114
+ ## Errors
115
+
116
+ Malformed files raise `ValueError` with the parser's message; unreadable paths
117
+ raise `OSError`. `write` never raises for a missing picture link — it reports the
118
+ paths in `stats.failures` and still writes the Markdown.
119
+
120
+ ## Tests
121
+
122
+ ```bash
123
+ pip install -r requirements-dev.txt # maturin, pytest, twine
124
+ python -m pytest tests -q
125
+ ```
126
+
127
+ The fixtures come from `../tests/fixtures` plus `tests/data/notebook`, a small
128
+ notebook tree (a table of contents, a group and a nested group). The package
129
+ tests build their cabinet in Python, independently of the Rust reader.
130
+
131
+ ## Limitations
132
+
133
+ Same as the CLI: `[MS-ONESTORE]` revision stores are fully read, `[MS-FSSHTTPB]`
134
+ package stores are marked experimental, encrypted sections are reported but not
135
+ decrypted, and cabinet folders compressed with Quantum or LZX are rejected.
136
+