From d2ebc34ed72c7bcb67585616550f0de0bbea00d0 Mon Sep 17 00:00:00 2001 From: klangme1ster <126453713+klangme1ster@users.noreply.github.com> Date: Thu, 24 Sep 2026 15:04:09 +0200 Subject: [PATCH] Fix (convert): Remove the page's title, date and time lines only when present The first 6 lines of pandoc's output were always discarded, assuming they are the page's title, date and time. Pages without a date and time (e.g. emails sent to OneNote) lost real content, e.g. a table and a Subject line, or a page's first command. The new Remove-PageHeader removes the header only when there is a date line (text with a 4-digit year, any language) followed by a time line (e.g. '09:50', '9:19 AM'); the title may be empty. Without such a header, only a first line repeating the page's name is removed. Also fix the Created/Modified offset: the 'zz00' format printed only the offset's hours, which is wrong for e.g. +05:30. The output format stays the same (e.g. '+0200'). Co-Authored-By: Claude Opus 5.5 --- ConvertOneNote2MarkDown-v2.Tests.ps1 | 49 +++++++++++++++++++++ ConvertOneNote2MarkDown-v2.ps1 | 64 +++++++++++++++++++++++----- 2 files changed, 102 insertions(+), 11 deletions(-) diff --git a/ConvertOneNote2MarkDown-v2.Tests.ps1 b/ConvertOneNote2MarkDown-v2.Tests.ps1 index 72d1ae7..76a6a0d 100644 --- a/ConvertOneNote2MarkDown-v2.Tests.ps1 +++ b/ConvertOneNote2MarkDown-v2.Tests.ps1 @@ -407,6 +407,55 @@ Describe "Encode-Markdown" -Tag 'Unit' { } +Describe "Remove-PageHeader" -Tag 'Unit' { + + Context 'Behavior' { + + It "Should remove the title, date and time lines ()" -TestCases @( + @{ Name = 'German'; Lines = @('Docker', '', 'Donnerstag, 17. Oktober 2024', '', '09:50', '', 'body', 'more') } + @{ Name = 'English, AM/PM'; Lines = @('Max Planck', '', 'Monday, September 8, 2014', '', '9:19 AM', '', 'body', 'more') } + @{ Name = 'title with a year'; Lines = @('Pressemeetings 2011', '', 'Saturday, October 29, 2011', '', '6:22 AM', '', 'body', 'more') } + @{ Name = 'untitled page'; Lines = @([string][char]0x00A0, '', 'Dienstag, 19. August 2025', '', '12:34', '', 'body', 'more') } + ) { + param ($Name, $Lines) + + $result = @( Remove-PageHeader -Lines $Lines ) + + ($result -join "`n").Trim() | Should -Be "body`nmore" + } + + It "Should keep all lines if there is no date and time (e.g. an email sent to OneNote)" { + $lines = @('Linksammlung', '', '| **Tags:** #Teufel |', '|-------------------|', '', '**Subject:** Linksammlung', 'body') + + $result = @( Remove-PageHeader -Lines $lines ) + + $result | Should -Be $lines + } + + It "Should remove only a leading title line if there is no date and time, but a -Title" { + $lines = @('curl \> /volume1/extern', '', 'curl \> /volume1/external_ip.log', 'body') + + $result = @( Remove-PageHeader -Lines $lines -Title 'curl http://ifconfig.me/ip > /volume1/extern' ) + + ($result -join "`n").Trim() | Should -Be "curl \> /volume1/external_ip.log`nbody" + } + + It "Should return an empty string for a page that only has a header" { + $result = @( Remove-PageHeader -Lines @('Docker', '', 'Donnerstag, 17. Oktober 2024', '', '09:50') ) + + $result | Should -Be @('') + } + + It "Should accept empty input" { + $result = @( Remove-PageHeader -Lines @() ) + + $result.Count | Should -Be 0 + } + + } + +} + Describe "New-OneNoteConnection" -Tag 'Unit' { Context 'Behavior' { diff --git a/ConvertOneNote2MarkDown-v2.ps1 b/ConvertOneNote2MarkDown-v2.ps1 index 3668cba..eb62542 100644 --- a/ConvertOneNote2MarkDown-v2.ps1 +++ b/ConvertOneNote2MarkDown-v2.ps1 @@ -497,6 +497,54 @@ Function Encode-Markdown { $Name } +Function Remove-PageHeader { + [CmdletBinding()] + param ( + # The markdown lines produced by pandoc from a published page + [Parameter(Mandatory)] + [AllowEmptyCollection()] + [AllowEmptyString()] + [string[]] + $Lines + , + # The page's name. Without a date/time header, a first line matching it is removed too (we add our own heading) + [Parameter()] + [string] + $Title = '' + ) + + # A page published to docx starts with its title, date and time paragraphs (e.g. 'Docker', 'Donnerstag, 17. Oktober 2024', '09:50'), + # which pandoc renders as the first lines of the markdown. We add our own heading, so remove them - but only when they are really + # there: some pages (e.g. emails sent to OneNote) have no date/time paragraphs, and blindly dropping the first 6 lines removed real content. + # The title may be empty (an untitled page yields a line with just a non-breaking space). + $datePattern = '^(?=.*\p{L})(?=.*\b\d{4}\b)[^|#>*]{1,80}$' # Some text with a 4-digit year, in any locale + $timePattern = '^\d{1,2}:\d{2}(:\d{2})?(\s*[AaPp]\.?\s?[Mm]\.?)?$' # E.g. '09:50', '9:19 AM' + $clean = { param($s) $s.Replace([string][char]0x00A0, ' ').Trim() } + $lettersAndDigits = { param($s) $s -replace '[^\p{L}\p{N}]', '' } # Pandoc escapes/autolinks the title text, so compare only letters and digits + $nonBlank = @( + for ($i = 0; $i -lt $Lines.Count -and $i -lt 8; $i++) { + if (& $clean $Lines[$i]) { + $i + } + } + ) + $lastHeaderLine = -1 + if ($nonBlank.Count -ge 3 -and (& $clean $Lines[$nonBlank[1]]) -match $datePattern -and (& $clean $Lines[$nonBlank[2]]) -match $timePattern) { + $lastHeaderLine = $nonBlank[2] # Title, date, time + }elseif ($nonBlank.Count -ge 2 -and (& $clean $Lines[$nonBlank[0]]) -match $datePattern -and (& $clean $Lines[$nonBlank[1]]) -match $timePattern) { + $lastHeaderLine = $nonBlank[1] # Empty title, date, time + }elseif ($Title -and $nonBlank.Count -ge 1 -and (& $lettersAndDigits $Lines[$nonBlank[0]]) -and (& $lettersAndDigits $Lines[$nonBlank[0]]) -eq (& $lettersAndDigits $Title)) { + $lastHeaderLine = $nonBlank[0] # Title only + } + if ($lastHeaderLine -lt 0) { + $Lines + }elseif ($lastHeaderLine + 1 -lt $Lines.Count) { + $Lines[($lastHeaderLine + 1)..($Lines.Count - 1)] + }else { + '' + } +} + Function Set-ContentNoBom { [CmdletBinding()] param ( @@ -920,8 +968,9 @@ Function New-SectionGroupConversionConfig { replacement = & { $heading = "# $( $pageCfg['object'].name )" if ($config['headerTimestampEnabled']['value'] -eq 1) { - $heading += "`n`nCreated: $( $pageCfg['dateTime'].ToString('yyyy-MM-dd HH:mm:ss zz00') )" - $heading += "`n`nModified: $( $pageCfg['lastModifiedTime'].ToString('yyyy-MM-dd HH:mm:ss zz00') )" + # 'zzz' without its colon (e.g. '+0200'). The former 'zz00' printed only the offset's hours, which is wrong for e.g. +05:30 + $heading += "`n`nCreated: $( $pageCfg['dateTime'].ToString('yyyy-MM-dd HH:mm:ss ') + $pageCfg['dateTime'].ToString('zzz').Replace(':', '') )" + $heading += "`n`nModified: $( $pageCfg['lastModifiedTime'].ToString('yyyy-MM-dd HH:mm:ss ') + $pageCfg['lastModifiedTime'].ToString('zzz').Replace(':', '') )" $heading += "`n`n---`n`n" } $heading @@ -1273,15 +1322,8 @@ Function Convert-OneNotePage { if (!$config['dryRun']['value']) { # Get markdown content $content = @( Get-Content -LiteralPath $pageCfg['filePath'] -ErrorAction Stop ) # Use -LiteralPath so that characters like '(', ')', '[', ']', '`', "'", '"' are supported. Or else we will get an error "Cannot find path 'xxx' because it does not exist" - $content = @( - if ($content.Count -gt 6) { - # Discard first 6 lines which contain a header, created date, and time. We are going to add our own header - $content[6..($content.Count - 1)] - }else { - # Empty page - '' - } - ) -join "`n" + # Discard the page's title, date and time lines (if present). We are going to add our own header + $content = @( Remove-PageHeader -Lines $content -Title $pageCfg['object'].name ) -join "`n" } # Mutate