diff --git a/Config/Get-OneNotePublishConfiguration.psm1 b/Config/Get-OneNotePublishConfiguration.psm1 index 09e9fb1..a6592a1 100644 --- a/Config/Get-OneNotePublishConfiguration.psm1 +++ b/Config/Get-OneNotePublishConfiguration.psm1 @@ -40,7 +40,7 @@ function Export-OneNoteConfigCheckKeyValue { if ($Type -eq "Path") { if ((Test-Path -Path $Value) -ne $true) { - New-Dir -Path $path + New-Dir -Path $Value } $Config | Add-Member -Force -Type NoteProperty -Name $Key -Value $Value.Trim() } @@ -85,7 +85,8 @@ function Get-OneNotePublishConfiguration { $TagPath = Join-Path -Path $config.ExportRootPath -ChildPath "_tags" New-Dir -Path $TagPath # delete the tag items on each run since otherwise they will be appended to - (Get-ChildItem -Path $config.ExportRootPath -Recurse -Filter '*.tag.html').Fullname | Remove-Item + $tagFiles = (Get-ChildItem -Path $config.ExportRootPath -Recurse -Filter '*.tag.html' -ErrorAction SilentlyContinue).Fullname + if ($null -ne $tagFiles) { $tagFiles | Remove-Item } $config | Add-Member -Type NoteProperty -Name PublishFormats -Value (Get-OneNotePublishFormats) -Force -PassThru | Add-Member -Type NoteProperty -Name pandocMdFormats -Value (Get-PandocMDOutputFormats) -Force -PassThru | @@ -98,6 +99,19 @@ function Get-OneNotePublishConfiguration { } } } + else { + foreach($exportFormat in $config.ExportFormat) { + if (-not ($config.PublishFormats -contains $exportFormat)) { + $pandocPath = (Get-Command pandoc -ErrorAction SilentlyContinue).Source + if ($null -ne $pandocPath) { + $config | Add-Member -Type NoteProperty -Name 'Pandoc' -Value $pandocPath -Force + } + else { + Write-Host "Pandoc not found in PATH and AutoDownloadPandoc is False. Markdown export will fail." -ForegroundColor Red + } + } + } + } foreach($exportFormat in $config.ExportFormat) { if ($config.PandocMdFormats -contains $exportFormat) { diff --git a/Config/publish.cfg b/Config/publish.cfg index 749212d..0ebabb1 100644 --- a/Config/publish.cfg +++ b/Config/publish.cfg @@ -91,7 +91,7 @@ ExportRootPath = e:\\exportonenote3 # # if you select a markdown format docx is always added automagically # -ExportFormat = pdf, htm, docx, doc, one, markdown +ExportFormat = pdf, html, docx, doc, one, markdown # # If already OneNote Published files should be overwritten or not @@ -108,7 +108,7 @@ OverWriteAttachments = False # AutoDownload Pandoc if needed, if you already have this installed you can # set it to false # -AutoDownloadPandoc = True +AutoDownloadPandoc = False # # Only applicable to Markdown: diff --git a/ExportOneNoteHierarchy.ps1 b/ExportOneNoteHierarchy.ps1 index d6c2d65..51f7599 100644 --- a/ExportOneNoteHierarchy.ps1 +++ b/ExportOneNoteHierarchy.ps1 @@ -5,10 +5,20 @@ .NOTES - make sure to have the OneNote Client open - edit the config.cfg file with preferences + - requires Windows PowerShell 5.1 (OneNote COM doesn't work in PS 7+) #> + +# Re-launch in Windows PowerShell 5.1 if running in PS 7+ +if ($PSVersionTable.PSVersion.Major -ge 6) { + Write-Host "OneNote COM requires Windows PowerShell 5.1. Re-launching..." + powershell.exe -ExecutionPolicy Bypass -File $MyInvocation.MyCommand.Path + exit $LASTEXITCODE +} + (Get-ChildItem -Path "$PSScriptRoot\Modules" -Recurse -Filter '*.psm1' -Verbose).FullName | ForEach-Object { Import-Module $_ -Force } (Get-ChildItem -Path "$PSScriptRoot\ScriptModules" -Recurse -Filter '*.psm1' -Verbose).FullName | ForEach-Object { Import-Module $_ -Force } (Get-ChildItem -Path "$PSScriptRoot\Config" -Recurse -Filter '*.psm1' -Verbose).FullName | ForEach-Object { Import-Module $_ -Force } $config = Get-OneNotePublishConfiguration -Path "$PSScriptRoot\Config\publish.cfg" -Publish-OneNoteHierarchyPages -Config $config \ No newline at end of file + +Publish-OneNoteHierarchyPages -Config $config diff --git a/Modules/OneNote/Application/OneNoteGetPageContent.psm1 b/Modules/OneNote/Application/OneNoteGetPageContent.psm1 index f6e34fc..834dbeb 100644 --- a/Modules/OneNote/Application/OneNoteGetPageContent.psm1 +++ b/Modules/OneNote/Application/OneNoteGetPageContent.psm1 @@ -20,7 +20,7 @@ function Get-OneNotePageInsertedFileObjects { $name = $insertedFile.InsertedFile.preferredName | Remove-InvalidFileNameCharsInsertedFiles $destination = Join-Path -Path $AttachmentsPath -ChildPath $name $destinationExists = Test-Path -Path $destination - if (($true -eq $destinationExists -and $Config.OverwriteAttachments -eq $true) -or ($false -eq $destinationExists)) { + if (($true -eq $destinationExists -and $OverwriteAttachments -eq $true) -or ($false -eq $destinationExists)) { New-Item -Path $AttachmentsPath -ItemType "directory" -Force | Out-Null # not completely safe but works for me: if ($destination.Length -gt 259) { diff --git a/Modules/OneNote/Hierarchy/OneNoteHierarchy.psm1 b/Modules/OneNote/Hierarchy/OneNoteHierarchy.psm1 index 98ea7a6..361f52f 100644 --- a/Modules/OneNote/Hierarchy/OneNoteHierarchy.psm1 +++ b/Modules/OneNote/Hierarchy/OneNoteHierarchy.psm1 @@ -37,13 +37,14 @@ function Get-OneNoteHierarchy { try { [xml]$ObjectId = $null $OneNote = New-Object -ComObject OneNote.Application - $OneNote.GetHierarchy("", [Microsoft.Office.InterOp.OneNote.HierarchyScope]::hsPages, [ref]$ObjectId) + [string]$xmlString = "" + $OneNote.GetHierarchy("", 4, [ref]$xmlString) + $ObjectId = [xml]$xmlString [System.Runtime.Interopservices.Marshal]::ReleaseComObject($OneNote) | Out-Null Remove-Variable OneNote return $ObjectId } - catch - { + catch { Throw } } \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ba9daa --- /dev/null +++ b/README.md @@ -0,0 +1,151 @@ +# OneNote Exporter + +A PowerShell-based tool that exports all OneNote notebooks to various file formats via the OneNote COM API. Designed for bulk extraction of OneNote content into files suitable for AI knowledge base ingestion, backup, or migration. + +## What It Does + +- Exports all open OneNote notebooks (sections, section groups, and pages) to one or more output formats +- Extracts page attachments (embedded files) to a dedicated `_attachments` folder +- Collects OneNote tags (e.g., To Do items) and exports them as HTML summaries +- Converts exported DOCX files to Markdown via Pandoc with configurable options +- Preserves the notebook/section/page hierarchy as a directory structure +- Renames images in Markdown output with unique, descriptive filenames +- Produces a timestamped log of each export run + +## Supported Export Formats + +| Format | Extension | Description | +|--------|-----------|-------------| +| one | .one | OneNote format | +| onepkg | .onepkg | OneNote package | +| mht | .mht | Web page archive (MHTML) | +| pdf | .pdf | PDF | +| xps | .xps | XML Paper Specification | +| docx | .docx | Microsoft Word (modern) | +| doc | .doc | Microsoft Word (classic) | +| emf | .emf | Enhanced Metafile | +| htm | .htm | HTML with images | + +### Pandoc Markdown Formats + +| Format | Description | +|--------|-------------| +| markdown | Pandoc extended Markdown | +| markdown_mmd | MultiMarkdown | +| markdown_phpextra | PHP Markdown Extra | +| markdown_strict | Original Markdown | +| commonmark | CommonMark | +| commonmark_x | CommonMark with extensions | +| gfm | GitHub Flavored Markdown | +| markdown_github | GitHub Markdown with extensions | + +Markdown formats require Pandoc. If a markdown format is selected, DOCX is automatically included as an intermediate step. + +## Requirements + +- Windows PowerShell 5.1 (the script auto-relaunches in 5.1 if started from PowerShell 7+) +- Microsoft OneNote desktop (Office 2013/2016/365 — the COM API version, not the Store app) +- OneNote must be running with notebooks open +- [Pandoc](https://pandoc.org/) installed and in PATH (only required for Markdown export) + +## Usage + +1. Edit `Config/publish.cfg` with your preferences +2. Open OneNote and ensure your notebooks are loaded +3. Run one of the two commands. Windows PowerShell usually blocks running of scripts. + +```powershell +.\ExportOneNoteHierarchy.ps1 +``` + +```powershell +Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass +.\ExportOneNoteHierarchy.ps1 +``` + +## Configuration + +Edit `Config/publish.cfg`: + +```ini +# Output directory +ExportRootPath = e:\\exportonenote3 + +# Formats to export (comma-separated) +ExportFormat = markdown + +# Overwrite existing exported files +Overwrite = False + +# Overwrite existing attachments +OverWriteAttachments = False + +# Auto-download Pandoc if not installed +AutoDownloadPandoc = False + +# Markdown options +MdClearSpaces = True +MdClearEscape = True +MdCentralMediaPath = True +MdAddYaml = True +``` + +## Output Structure + +```text +/ +├── / # One folder per export format +│ └── / +│ └──
/ +│ └── . +├── _attachments/ # Embedded file attachments +│ └── / +│ └──
/ +│ └── / +├── _tags/ # Tag summaries as HTML +│ └── .tag.html +└── ExportOneNote_.log +``` + +## Project Structure + +```text +ExportOneNoteHierarchy.ps1 # Entry point +Config/ + publish.cfg # User configuration + Get-OneNotePublishConfiguration.psm1 # Config parser and validator +Modules/ + Config/Config.psm1 # Generic config file reader + IO/FileOperations.psm1 # File/directory helpers + OneNote/ + Application/ + OneNotePublish.psm1 # COM publish calls + OneNoteGetPageContent.psm1 # Page content and attachment extraction + Hierarchy/ + OneNoteHierarchy.psm1 # GetHierarchy COM call + OneNoteNotebook*.psm1 # Notebook/collection accessors + OneNoteSection*.psm1 # Section/collection accessors + OneNotePage*.psm1 # Page/collection accessors + Pandoc/ + Pandoc.psm1 # Pandoc download and invocation + Invoke-MDFilters.psm1 # Post-conversion Markdown cleanup +ScriptModules/ + Publish-OneNoteHierarchyPages.psm1 # Main orchestration loop + Get-OneNotePageCollectionFromHierarchy.psm1 # Hierarchy traversal + Get-OneNotePublishPaths.psm1 # Output path generation + Publish-OneNotePageAttachments.psm1 # Attachment export + Publish-OneNoteTags.psm1 # Tag export + Get-OneNoteTags.psm1 # Tag extraction from page XML + ConvertTo-MdFromDocx.psm1 # Pandoc DOCX-to-MD conversion +``` + +## Known Limitations + +- Encrypted sections/pages cannot be exported +- The OneNote COM API does not work in PowerShell 7+ (uses .NET Core which lacks proper COM marshaling for OneNote) +- Very long page names (>259 characters) are truncated to avoid Windows path limits +- Collapsed page sub-hierarchies are exported normally (they are not skipped) + +## Credits + +Forked from [SjoerdV/ConvertOneNote2MarkDown](https://github.com/SjoerdV/ConvertOneNote2MarkDown) with input from [nixsee/ConvertOneNote2MarkDown](https://github.com/nixsee/ConvertOneNote2MarkDown). diff --git a/ScriptModules/Get-OneNotePageCollectionFromHierarchy.psm1 b/ScriptModules/Get-OneNotePageCollectionFromHierarchy.psm1 index 0f0d93d..4525434 100644 --- a/ScriptModules/Get-OneNotePageCollectionFromHierarchy.psm1 +++ b/ScriptModules/Get-OneNotePageCollectionFromHierarchy.psm1 @@ -163,7 +163,7 @@ function Get-OneNotePageCollectionFromHierarchy { #> try { $hierarchy = Get-OneNoteHierarchy - $notebookCollection = Get-OneNoteNoteBookCollection -Hierarchy $hierarchy + $notebookCollection = Get-OneNoteNotebookCollection -Hierarchy $hierarchy $TagsTable = @{} return Get-OneNotePageCollectionFromNotebookCollection -NotebookCollection $notebookCollection -TagsTable $TagsTable } diff --git a/ScriptModules/Publish-OneNoteHierarchyPages.psm1 b/ScriptModules/Publish-OneNoteHierarchyPages.psm1 index 07bb39d..937c9d7 100644 --- a/ScriptModules/Publish-OneNoteHierarchyPages.psm1 +++ b/ScriptModules/Publish-OneNoteHierarchyPages.psm1 @@ -8,10 +8,8 @@ function Publish-OneNoteHierarchyPages { param ( [PSCustomObject]$Config ) - $Config - $enrichedPageCollection, $tagsTable = Get-OneNotePageCollectionFromHierarchy - Write-Host "Done fetching pages. Fetched $($pageCollection.count) pages" -ForegroundColor Green + Write-Host "Done fetching pages. Fetched $($enrichedPageCollection.count) pages" -ForegroundColor Green Publish-OneNoteTags -Config $Config -TagsTable $tagsTable foreach ($page in $enrichedPageCollection) { $OutputEncoding = [ System.Text.Encoding]::UTF8