From 42c05415b6f97c6adeaf1d13b67a80003ec8f675 Mon Sep 17 00:00:00 2001 From: klangme1ster <126453713+klangme1ster@users.noreply.github.com> Date: Thu, 24 Sep 2026 15:05:03 +0200 Subject: [PATCH] Fix (file names): Decode OneNote's escape codes in section names OneNote reports characters that aren't allowed in section file names as '^' codes in the section's name, e.g. 'AI ^M ML' for a section named 'AI / ML', and 'DECT^J Bluetooth' for 'DECT & Bluetooth'. These codes ended up in folder names ('AI-^M--ML'). Decode '^M' to '/' and '^J' to '&' (the codes observed so far) before removing invalid file name characters, for notebook, section group and section names. Co-Authored-By: Claude Opus 5.5 --- ConvertOneNote2MarkDown-v2.Tests.ps1 | 18 ++++++++++++++++++ ConvertOneNote2MarkDown-v2.ps1 | 20 ++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/ConvertOneNote2MarkDown-v2.Tests.ps1 b/ConvertOneNote2MarkDown-v2.Tests.ps1 index 72d1ae7..0fc7b37 100644 --- a/ConvertOneNote2MarkDown-v2.Tests.ps1 +++ b/ConvertOneNote2MarkDown-v2.Tests.ps1 @@ -372,6 +372,24 @@ Describe "Remove-InvalidFileNameCharsInsertedFiles" -Tag 'Unit' { } +Describe "ConvertFrom-OneNoteEscapedName" -Tag 'Unit' { + + Context 'Behavior' { + + It "Should decode OneNote's '^' codes in section names" { + 'AI ^M ML' | ConvertFrom-OneNoteEscapedName | Should -Be 'AI / ML' + 'Messen^J Konferenzen^J Seminare' | ConvertFrom-OneNoteEscapedName | Should -Be 'Messen& Konferenzen& Seminare' + 'plain name' | ConvertFrom-OneNoteEscapedName | Should -Be 'plain name' + } + + It "Should turn a decoded '/' into a dash in the directory name" { + 'AI ^M ML' | ConvertFrom-OneNoteEscapedName | Remove-InvalidFileNameChars | Should -Not -Match '\^' + } + + } + +} + Describe "Encode-Markdown" -Tag 'Unit' { Context 'Parameters' { diff --git a/ConvertOneNote2MarkDown-v2.ps1 b/ConvertOneNote2MarkDown-v2.ps1 index 3668cba..04b1cc5 100644 --- a/ConvertOneNote2MarkDown-v2.ps1 +++ b/ConvertOneNote2MarkDown-v2.ps1 @@ -465,6 +465,22 @@ Function Remove-InvalidFileNameCharsInsertedFiles { return $newName } +Function ConvertFrom-OneNoteEscapedName { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true,Position = 0,ValueFromPipeline = $true)] + [AllowEmptyString()] + [string] + $Name + ) + + process { + # OneNote stores characters that aren't allowed in its section file names as '^' codes in the section's name, + # e.g. a section named 'AI / ML' is reported as 'AI ^M ML', and 'DECT & Bluetooth' as 'DECT^J Bluetooth' + $Name.Replace('^M', '/').Replace('^J', '&') + } +} + Function Encode-Markdown { [CmdletBinding()] param ( @@ -681,7 +697,7 @@ Function New-SectionGroupConversionConfig { $cfg['object'] = $sectionGroup # Keep a reference to the SectionGroup object $cfg['kind'] = 'SectionGroup' $cfg['id'] = $sectionGroup.ID # E.g. {9570CCF6-17C2-4DCE-83A0-F58AE8914E29}{1}{B0} - $cfg['nameCompat'] = $sectionGroup.name | Remove-InvalidFileNameChars + $cfg['nameCompat'] = $sectionGroup.name | ConvertFrom-OneNoteEscapedName | Remove-InvalidFileNameChars $cfg['levelsFromRoot'] = $LevelsFromRoot $cfg['uri'] = $sectionGroup.path # E.g. https://d.docs.live.net/0123456789abcdef/Skydrive Notebooks/mynotebook/mysectiongroup $cfg['notesDirectory'] = [io.path]::combine( $NotesDestination.TrimEnd('/').TrimEnd('\').Replace('\', [io.path]::DirectorySeparatorChar), $cfg['nameCompat'] ) # No need to truncate. Section Group and Section names have a max length of 50, so we should never hit the absolute path, file name, or directory name limits on Windows @@ -720,7 +736,7 @@ Function New-SectionGroupConversionConfig { $sectionCfg['object'] = $section # Keep a reference to the Section object $sectionCfg['kind'] = 'Section' $sectionCfg['id'] = $section.ID # E.g {BE566C4F-73DC-43BD-AE7A-1954F8B22C2A}{1}{B0} - $sectionCfg['nameCompat'] = $section.name | Remove-InvalidFileNameChars + $sectionCfg['nameCompat'] = $section.name | ConvertFrom-OneNoteEscapedName | Remove-InvalidFileNameChars $sectionCfg['levelsFromRoot'] = $cfg['levelsFromRoot'] + 1 $sectionCfg['pathFromRoot'] = "$( $cfg['pathFromRoot'] )$( [io.path]::DirectorySeparatorChar )$( $sectionCfg['nameCompat'] )".Trim([io.path]::DirectorySeparatorChar) # No need to truncate. Section Group and Section names have a max length of 50, so we should never hit the absolute path, file name, or directory name limits on Windows $sectionCfg['pathFromRootCompat'] = $sectionCfg['pathFromRoot'] | Remove-InvalidFileNameChars