From 23f0f23fc7064901d2942eb0d0ff5c4adea3afd6 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Sat, 12 Sep 2026 15:01:41 -0700 Subject: [PATCH] feat: edit cell shading and indents and nest tables through the page model Shading and indents change in place on a stored cell: the writer sets or removes the documented CellShadingColor and the indent array on the cell object. OneNote 2010 stores the shading property but neither renders it nor accepts it through its COM schema (corpus/m6/cell-shading-control-01 already records both), so the property is written as documented without a native rendering claim. Cell layout still stays fixed, as OneNote sizes cells itself. A table inside a cell paragraph needed no new lowering: the placement and structure passes already recurse through cells. corpus/table-edit/nested is the native gate, a one-column table written into a cell of the native tree fixture and cold-read by OneNote 2010 with both tables; tools/test_table_edit.py checks it without a VM. Assisted-by: claude-fable-5.1 --- corpus/table-edit/README.md | 15 +- .../nested/candidate/Open Notebook.onetoc2 | Bin 0 -> 1612 bytes .../table-edit/nested/candidate/synthetic.one | Bin 0 -> 91192 bytes corpus/table-edit/nested/cold/commands.jsonl | 3 + corpus/table-edit/nested/cold/machine.json | 1 + .../cold/notebook/Open Notebook.onetoc2 | Bin 0 -> 3780 bytes .../nested/cold/notebook/synthetic.one | Bin 0 -> 94920 bytes .../nested/cold/read/environment.json | 7 + .../table-edit/nested/cold/read/hierarchy.xml | 2 + .../table-edit/nested/cold/read/page-000.xml | 5 + .../table-edit/nested/cold/read/page-001.xml | 5 + .../table-edit/nested/cold/read/page-002.xml | 5 + .../table-edit/nested/cold/read/page-003.xml | 5 + .../table-edit/nested/cold/read/page-004.xml | 5 + .../table-edit/nested/cold/read/page-005.xml | 5 + .../table-edit/nested/cold/read/page-006.xml | 5 + .../table-edit/nested/cold/read/page-007.xml | 5 + .../table-edit/nested/cold/read/page-008.xml | 4 + .../table-edit/nested/cold/read/page-009.xml | 5 + .../table-edit/nested/cold/read/page-010.xml | 20 +++ .../table-edit/nested/cold/read/page-011.xml | 4 + .../table-edit/nested/cold/read/payloads.json | 3 + corpus/table-edit/nested/cold/run.json | 18 +++ .../table-edit/nested/cold/scripts/cold.ps1 | 27 ++++ .../table-edit/nested/cold/scripts/read.ps1 | 142 ++++++++++++++++++ corpus/table-edit/nested/cold/source.json | 14 ++ corpus/table-edit/nested/cold/teardown.json | 1 + crates/notebook/README.md | 2 +- crates/onestore/README.md | 2 +- crates/onestore/src/page/write.rs | 69 ++++++++- crates/onestore/tests/page_tables.rs | 86 +++++++++++ tools/test_table_edit.py | 6 + 32 files changed, 462 insertions(+), 9 deletions(-) create mode 100644 corpus/table-edit/nested/candidate/Open Notebook.onetoc2 create mode 100644 corpus/table-edit/nested/candidate/synthetic.one create mode 100644 corpus/table-edit/nested/cold/commands.jsonl create mode 100644 corpus/table-edit/nested/cold/machine.json create mode 100644 corpus/table-edit/nested/cold/notebook/Open Notebook.onetoc2 create mode 100644 corpus/table-edit/nested/cold/notebook/synthetic.one create mode 100644 corpus/table-edit/nested/cold/read/environment.json create mode 100644 corpus/table-edit/nested/cold/read/hierarchy.xml create mode 100644 corpus/table-edit/nested/cold/read/page-000.xml create mode 100644 corpus/table-edit/nested/cold/read/page-001.xml create mode 100644 corpus/table-edit/nested/cold/read/page-002.xml create mode 100644 corpus/table-edit/nested/cold/read/page-003.xml create mode 100644 corpus/table-edit/nested/cold/read/page-004.xml create mode 100644 corpus/table-edit/nested/cold/read/page-005.xml create mode 100644 corpus/table-edit/nested/cold/read/page-006.xml create mode 100644 corpus/table-edit/nested/cold/read/page-007.xml create mode 100644 corpus/table-edit/nested/cold/read/page-008.xml create mode 100644 corpus/table-edit/nested/cold/read/page-009.xml create mode 100644 corpus/table-edit/nested/cold/read/page-010.xml create mode 100644 corpus/table-edit/nested/cold/read/page-011.xml create mode 100644 corpus/table-edit/nested/cold/read/payloads.json create mode 100644 corpus/table-edit/nested/cold/run.json create mode 100644 corpus/table-edit/nested/cold/scripts/cold.ps1 create mode 100644 corpus/table-edit/nested/cold/scripts/read.ps1 create mode 100644 corpus/table-edit/nested/cold/source.json create mode 100644 corpus/table-edit/nested/cold/teardown.json diff --git a/corpus/table-edit/README.md b/corpus/table-edit/README.md index d96fe5fce55b69f51424cb25b55ee36881fb0316..e3d23c4df2c2041d42362d04e84651ee6e6b46af 100644 --- a/corpus/table-edit/README.md +++ b/corpus/table-edit/README.md @@ -16,13 +16,20 @@ of three cells. `edited/cold` is its cold read: the native columns keep their widths and locks, OneNote sizes the unlocked column to its content, and the new row and cells read back with their text. +`nested/candidate` is the writer's output for +`a_table_nests_inside_a_cell_and_is_removed_again` on the same tree fixture: +the second cell of the native table gains a paragraph holding a one-column, +two-row table with locked 72 pt column and visible borders. `nested/cold` is +its cold read with both tables. + Native rows and cells are plain containers; the writer creates them with the modification time, the indent array copied from a sibling cell, and the flags every native cell carries, rebuilds each table's row and cell order to the model's, and writes the column count, widths, locks and border flag. New tables start as an empty paragraph whose content the structure pass replaces. An emptied cell keeps a replacement paragraph, as the tree writer provides. -`tools/test_table_edit.py` checks both captures without a VM. Regenerate with -`ONESTORE_TABLE_EXPORT` and `ONESTORE_TABLE_EDIT_EXPORT` set to new absolute -directories while running the tests, then cold-open them with -`tools/native_runner.py OUTPUT COLD --expected-pages 1|12 --collect-notebook`. +`tools/test_table_edit.py` checks all three captures without a VM. Regenerate +with `ONESTORE_TABLE_EXPORT`, `ONESTORE_TABLE_EDIT_EXPORT` and +`ONESTORE_NESTED_TABLE_EXPORT` set to new absolute directories while running +the tests, then cold-open them with `tools/native_runner.py OUTPUT COLD +--expected-pages 1|12 --collect-notebook`. diff --git a/corpus/table-edit/nested/candidate/Open Notebook.onetoc2 b/corpus/table-edit/nested/candidate/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..ac6404565fb5d7dc4eecd7d50f52a80fe56ea39d GIT binary patch literal 1612 zcmZ3e|KIuM`!b(-j|5(YE0x6?)V&hBaxB(w-6QD(VLPYJVL$`+cV`JmPyFh+^n&=r zw;)KmcMh!UUi+OdL%Z&RtpfsshddG8EV#JRKl)fEB_{fKYJK2dV}p zF!O#$eq2VX*HRWEo~mXQahMyPtZEg>oh{?C_kt4_>vVrBnEFwAGz3O)2sE%TOp#@f z*(t*yk`1Ky0cinXaQxNx_^^X=kqi(QGgLCOon6z zJ%)UsNGebck-LGa8@PZ3Bf~ct1{GKiWMDY*<@?u-Rbfj6fvQ13q=2EJ{m<_M8x(|` z8n--td1@p7KbUIi7Yq%aS8`wS>4iDX$w@1}nwtpE%Mjg|Bt);G2txydN&=7o0(9d+ z;vi*y%G)yU{rv9IH?jZHgsl^IYX~qj=v6RuFtC7hF)%~`F~kcX9!LjBHUcUu0g?ra zgJeKB(g5gXC|d?-7C1CuA>zHuxaqLyp>CM}g@6h{fCuOpbO(YA6L4UdPyn?GY&uX3 zWEKd3^r4$43=#wajtM}0&~h>W$j4?j3l2LWrlXnj3~TB;0WT literal 0 HcmV?d00001 diff --git a/corpus/table-edit/nested/candidate/synthetic.one b/corpus/table-edit/nested/candidate/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..266fcc28db50f2d19f54b1f2b89daa011b440cdb GIT binary patch literal 91192 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 literal 0 HcmV?d00001 diff --git a/corpus/table-edit/nested/cold/commands.jsonl b/corpus/table-edit/nested/cold/commands.jsonl new file mode 100644 index 0000000000000000000000000000000000000000..ca3daf4c4d9f68f3eb732da5aa3784363c6c7bc6 --- /dev/null +++ b/corpus/table-edit/nested/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-8E63A852 -ExpectedPages 12", "exit": 0, "stdout": "Read 1 sections and 12 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-edit/nested/cold/machine.json b/corpus/table-edit/nested/cold/machine.json new file mode 100644 index 0000000000000000000000000000000000000000..af2731f4d9ddcf0999ea29a58d48135326fae541 --- /dev/null +++ b/corpus/table-edit/nested/cold/machine.json @@ -0,0 +1 @@ +{"name": "m6-8e63a852", "hostname": "ONE-M6-8E63A852"} diff --git a/corpus/table-edit/nested/cold/notebook/Open Notebook.onetoc2 b/corpus/table-edit/nested/cold/notebook/Open Notebook.onetoc2 new file mode 100644 index 0000000000000000000000000000000000000000..dd056869ac900f0254b6015f73f69cec7ce64d2d GIT binary patch literal 3780 zcmZ3e|KIuM`!b(-j|5(YE0x6?)V&hBaxB(w-6QD(VLPYJVL$`+cV`JmPyFh+^n&=r zw;)KmcMh!33Z*OdL%Z&RtpfsshddG8EV#JRKl)fEB_{08$`xKma6i zgbzd_K#AWzrkCbxoSWy$^jx2jV9pGcda|ljBzLxq%iar4T&&amtq?k}v$kk1;|Ltw~aV^h$K7qE{>yh?eX=IOhn*29CIiI9 z43!Ld3?&R145>gelOdTwk0Bo@k_wcAD+O@=`Qq!$8?eEI%$V^!D^ zK`39OfT5xN&+h{p6oi}_w>*A%Y9s$Ym}==43=N)Fa$oZ4g*na1Nh`man+>8E|7uVyNsI-iyrER`CkaA5CnLD zjzM=I$S?s1h6x2wtH7oM#lQs~w9G>{PZ(qv5O7QY>VuY(0YE-BvsrN1iOqaaJb^p~ zaxbVX2iXInL2g{Ot$xZ8kR%8I-3k;32LLG0n85K4N=qQsTjt#ZD`IHCp4QU1N~i5P z{K8|+h3!dEx`&!TrW2pmU||Y!D@dpGi-rcFK_k?Bp9qyT`g5-4r8hUbo$$%RvAvsz!^!Nb9Sh#>Dv9_cz^dJX3 z$OeWQO@?1GKx4t2QDQU%MneD^0?1hfRtAFdy(kcaGMwhz4;fIUqts{!jE2An2mt^a Cy-F4U literal 0 HcmV?d00001 diff --git a/corpus/table-edit/nested/cold/notebook/synthetic.one b/corpus/table-edit/nested/cold/notebook/synthetic.one new file mode 100644 index 0000000000000000000000000000000000000000..b1ade032e1a1d530a0dbf143c386c72784717362 GIT binary patch literal 94920 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 + diff --git a/corpus/table-edit/nested/cold/read/page-000.xml b/corpus/table-edit/nested/cold/read/page-000.xml new file mode 100644 index 0000000000000000000000000000000000000000..abe8e889db0e49cfacbcfec754d6dd8fe93ef84b --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-000.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-001.xml b/corpus/table-edit/nested/cold/read/page-001.xml new file mode 100644 index 0000000000000000000000000000000000000000..73fc668530cfe3c925ec3e9899ca78cb17d8e79c --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-001.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-002.xml b/corpus/table-edit/nested/cold/read/page-002.xml new file mode 100644 index 0000000000000000000000000000000000000000..33249a4a63862188d11be1dc660fb502d1046764 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-002.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-003.xml b/corpus/table-edit/nested/cold/read/page-003.xml new file mode 100644 index 0000000000000000000000000000000000000000..803070c943bb4c15bbde899ce815025e07ef30fb --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-003.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-004.xml b/corpus/table-edit/nested/cold/read/page-004.xml new file mode 100644 index 0000000000000000000000000000000000000000..1abeded6ec7686112d3fbf0321ee6a4523ed75e4 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-004.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-005.xml b/corpus/table-edit/nested/cold/read/page-005.xml new file mode 100644 index 0000000000000000000000000000000000000000..ab5eb2ff035e76901c4cb67a0a0c7c6650232807 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-005.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-006.xml b/corpus/table-edit/nested/cold/read/page-006.xml new file mode 100644 index 0000000000000000000000000000000000000000..f7e7c61fc66b3a05392b7a669554659eac7d9de7 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-006.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-007.xml b/corpus/table-edit/nested/cold/read/page-007.xml new file mode 100644 index 0000000000000000000000000000000000000000..5dce83b05f4dbb9134aa1f99de1655186742b0b5 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-007.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-008.xml b/corpus/table-edit/nested/cold/read/page-008.xml new file mode 100644 index 0000000000000000000000000000000000000000..5404646b65f0848851705698b95df7575d6e1307 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-008.xml @@ -0,0 +1,4 @@ + +Target 🦀 é Link label]]> diff --git a/corpus/table-edit/nested/cold/read/page-009.xml b/corpus/table-edit/nested/cold/read/page-009.xml new file mode 100644 index 0000000000000000000000000000000000000000..6f2d0a40c64f6c9f9d6708983f8a22623c0a07af --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-009.xml @@ -0,0 +1,5 @@ + +Target 🦀 é Link label]]>Child]]> diff --git a/corpus/table-edit/nested/cold/read/page-010.xml b/corpus/table-edit/nested/cold/read/page-010.xml new file mode 100644 index 0000000000000000000000000000000000000000..0ab9708e41484ef59f437b8d7c3809e8eba84602 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-010.xml @@ -0,0 +1,20 @@ + +Bold 🦀 italic é color 東京
+End
]]>
Fictitious: café, 東京, مرحبا]]>
diff --git a/corpus/table-edit/nested/cold/read/page-011.xml b/corpus/table-edit/nested/cold/read/page-011.xml new file mode 100644 index 0000000000000000000000000000000000000000..edc9181409326c6c543bce336887d83568b3c897 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/page-011.xml @@ -0,0 +1,4 @@ + +Target 🦀 é Link label]]> diff --git a/corpus/table-edit/nested/cold/read/payloads.json b/corpus/table-edit/nested/cold/read/payloads.json new file mode 100644 index 0000000000000000000000000000000000000000..1ce91a413d6fff4d58765187b76756f0e97ae843 --- /dev/null +++ b/corpus/table-edit/nested/cold/read/payloads.json @@ -0,0 +1,3 @@ +[ + +] \ No newline at end of file diff --git a/corpus/table-edit/nested/cold/run.json b/corpus/table-edit/nested/cold/run.json new file mode 100644 index 0000000000000000000000000000000000000000..07af38adfa7635acd5034a97c61a9f59db1580a0 --- /dev/null +++ b/corpus/table-edit/nested/cold/run.json @@ -0,0 +1,18 @@ +{ + "notebook": "/private/tmp/m4/nested", + "expected_pages": 12, + "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": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" + } +} diff --git a/corpus/table-edit/nested/cold/scripts/cold.ps1 b/corpus/table-edit/nested/cold/scripts/cold.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..a0b99e7aeb86f4dc10032537800501a52ad65b4f --- /dev/null +++ b/corpus/table-edit/nested/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-edit/nested/cold/scripts/read.ps1 b/corpus/table-edit/nested/cold/scripts/read.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..476094ab1e0c47077988a6461db2080cf208642b --- /dev/null +++ b/corpus/table-edit/nested/cold/scripts/read.ps1 @@ -0,0 +1,142 @@ +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) + $payloads += @{ page = $id; object = $file.ParentNode.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-edit/nested/cold/source.json b/corpus/table-edit/nested/cold/source.json new file mode 100644 index 0000000000000000000000000000000000000000..5e6c84c30728031d026ff42bdbfacfc80a21b384 --- /dev/null +++ b/corpus/table-edit/nested/cold/source.json @@ -0,0 +1,14 @@ +[ + { + "path": "Open Notebook.onetoc2", + "bytes": 1612, + "sha256": "1c7c0e30cc2c2f029f87e20bba0981f0bf8a7f2f6643ad67a24e80834f539991", + "mtime_ns": 1789250020753460025 + }, + { + "path": "synthetic.one", + "bytes": 91192, + "sha256": "6f3b8737c318834e2958368ed9fd9bc2ff4c7490bbd4a29907fa76e472355b43", + "mtime_ns": 1789250020752551397 + } +] diff --git a/corpus/table-edit/nested/cold/teardown.json b/corpus/table-edit/nested/cold/teardown.json new file mode 100644 index 0000000000000000000000000000000000000000..05a47793de40f322e745c4c4183727e3590ecf70 --- /dev/null +++ b/corpus/table-edit/nested/cold/teardown.json @@ -0,0 +1 @@ +{"absent": true} diff --git a/crates/notebook/README.md b/crates/notebook/README.md index cef70b51ababe2a8ec269f1d2196f7e8cec75249..aaffc0e1b263a86614981ebc249d2c33fd3b278e 100644 --- a/crates/notebook/README.md +++ b/crates/notebook/README.md @@ -14,7 +14,7 @@ diffs the supplied model against the page stored in the supplied local snapshot, writes the difference into the working image and queues one `Operation::Page(PageIntent { before, after, author })`; `before` is the page the edit started from and is the precondition reconciliation checks. Text, styles, -paragraph formatting, hyperlinks, bullets, numbering, note tags, table rows and columns, pictures (insertion in paragraphs or on the page, position, size, description) and attachments, paragraph structure, +paragraph formatting, hyperlinks, bullets, numbering, note tags, table rows, columns, cell shading, nested tables, pictures (insertion in paragraphs or on the page, position, size, description) and attachments, paragraph structure, outline layout, insertions and deletions are all differences between `before` and `after`; the library never sees editor operations. diff --git a/crates/onestore/README.md b/crates/onestore/README.md index 8d2e8936738494415ad65f951bb198b66ea11a96..3d1e9943f9e4e6cd00c6325acaaf8d5d436ea004 100644 --- a/crates/onestore/README.md +++ b/crates/onestore/README.md @@ -57,7 +57,7 @@ harness also accepts `--client-profile release`. | `ParagraphJoin`, `PreparedEdit::join` | Join adjacent ordinary text while preserving inherited character styles and native text-identity rules | | `TextAttribute`, `PreparedEdit::format` | Change character formatting over a UTF-16 range while sharing immutable styles; preserve unselected runs | | `OutlineEdit`, `PreparedEdit::outline` | Change ordinary outline position/width or a paragraph's saved expansion default, preserving identities and content | -| `PreparedEdit::page` | Publish an edited `page::Page` as one revision per changed space: text, character and paragraph formatting, hyperlinks, bullets and numbering, note tags, table rows and columns, inserted pictures (in paragraphs or on the page) and attachments, picture position, size and description, paragraph insertion/split/join/move/deletion, outline insertion/deletion/position/width and saved collapse state, lowered onto the typed writers with the model's identities | +| `PreparedEdit::page` | Publish an edited `page::Page` as one revision per changed space: text, character and paragraph formatting, hyperlinks, bullets and numbering, note tags, table rows, columns, cell shading and indents, nested tables, inserted pictures (in paragraphs or on the page) and attachments, picture position, size and description, paragraph insertion/split/join/move/deletion, outline insertion/deletion/position/width and saved collapse state, lowered onto the typed writers with the model's identities | | `TreeEdit`, `PreparedEdit::tree` | Move or delete a subtree on one page, normalize surviving containers, and replace an emptied table cell's paragraph atomically | | `PreparedEdit::commit`, `PreparedEdit::commit_file` | Publish the exact prepared image under caller-held exclusion or the conservative filesystem adapter | | `read_file` | Read a snapshot under whole-file exclusion | diff --git a/crates/onestore/src/page/write.rs b/crates/onestore/src/page/write.rs index 276698f1ccd1f69648b3a387612dc61882c88504..2435df64b5dee6f2b371230eba86850a3d2d0b3e 100644 --- a/crates/onestore/src/page/write.rs +++ b/crates/onestore/src/page/write.rs @@ -438,13 +438,11 @@ impl Lowering<'_> { .find(|before| before.id == cell.id) .is_none_or(|before| { cell.layout == before.layout - && cell.indents == before.indents - && cell.shading == before.shading && cell.unsupported == before.unsupported }) }); if !unchanged_cells { - return Err(invalid("Cell layout, indents and shading cannot be edited")); + return Err(invalid("Cell layout cannot be edited")); } } (ParagraphContent::Unsupported(a), ParagraphContent::Unsupported(b)) if a == b => {} @@ -690,6 +688,7 @@ impl Lowering<'_> { }); self.place_containers(old, new, placed, &existing)?; self.edit_table_structure(old, new)?; + self.edit_cells(old, new)?; self.edit_images(old, new)?; self.edit_attachments(old, new)?; self.place_containers(old, new, placed, &deferred)?; @@ -1052,6 +1051,70 @@ impl Lowering<'_> { /// order to the model's, and writes the column widths, locks and border flag. Native /// rows and cells are plain containers; a cell carries its indent array and the flags /// every native cell has. + /// Shading (the documented `CellShadingColor`, which OneNote 2010 stores but neither + /// renders nor accepts through its COM schema) and indents change in place on a cell. + fn edit_cells(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { + let stored: BTreeMap = old + .paragraphs + .values() + .filter_map(|paragraph| match ¶graph.content { + ParagraphContent::Table(table) => Some(table), + _ => None, + }) + .flat_map(|table| table.rows.iter().flat_map(|row| &row.cells)) + .map(|cell| (cell.id, cell)) + .collect(); + for paragraph in new.paragraphs.values() { + let ParagraphContent::Table(table) = ¶graph.content else { + continue; + }; + for cell in table.rows.iter().flat_map(|row| &row.cells) { + let Some(previous) = stored.get(&cell.id) else { + continue; + }; + if (cell.shading, &cell.indents) == (previous.shading, &previous.indents) { + continue; + } + let mut values: Values = vec![( + 0x14001d7a, + crate::create::current_timestamps()? + .0 + .to_le_bytes() + .to_vec(), + )]; + let mut removed = Vec::new(); + if cell.shading != previous.shading { + match cell.shading { + Some(shading) => values.push((0x14001e26, shading.to_le_bytes().to_vec())), + None => removed.push(0x14001e26), + } + } + if cell.indents != previous.indents { + if cell.indents.is_empty() { + removed.push(0x1c001c12); + } else { + values.push((0x1c001c12, measurement_bytes(&cell.indents, 4)?)); + } + } + let (space, object) = (self.space, self.id(cell.id)); + self.apply(|current| { + crate::write::write_revision(current, space, |raw| { + let mut object_properties = + PropertyObject::from_object(&raw.objects[&object])?; + object_properties.remove(&removed)?; + let values: Vec<(u32, &[u8])> = values + .iter() + .map(|(id, bytes)| (*id, bytes.as_slice())) + .collect(); + object_properties.set(&values)?; + Ok(BTreeMap::from([(object, object_properties)])) + }) + })?; + } + } + Ok(()) + } + fn edit_table_structure(&mut self, old: &View<'_>, new: &View<'_>) -> Result<(), Error> { let old_tables: BTreeMap = old .paragraphs diff --git a/crates/onestore/tests/page_tables.rs b/crates/onestore/tests/page_tables.rs index 5c9e8207172193b26225f7724a457c13662d878b..8d4541993d6a4bba2c2f7534d6ff23fcacd5d5a9 100644 --- a/crates/onestore/tests/page_tables.rs +++ b/crates/onestore/tests/page_tables.rs @@ -214,6 +214,92 @@ fn column_widths_locks_and_borders_change_in_place() { assert_same(written.as_bytes(), space, &after); } +#[test] +fn cell_shading_and_indents_change_in_place() { + let (space, before) = page_by_title(TREES, "Delete cell subtree"); + let mut after = before.clone(); + let table = table_mut(&mut after); + let cell = &mut table.rows[0].cells[0]; + assert_eq!(cell.shading, None); + cell.shading = Some(0x0000ffff); + let original_indents = cell.indents.clone(); + cell.indents = vec![18.0, 0.0, 27.0, 27.0]; + let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); + let stored = assert_same(written.as_bytes(), space, &after); + let mut reset = stored.clone(); + let cell = &mut table_mut(&mut reset).rows[0].cells[0]; + cell.shading = None; + cell.indents = original_indents; + let again = PreparedEdit::page(written.as_bytes(), space, &reset, AUTHOR).unwrap(); + assert_eq!(assert_same(again.as_bytes(), space, &reset), before); +} + +/// `ONESTORE_NESTED_TABLE_EXPORT` names a new directory receiving the candidate for a cold reopen. +#[test] +fn a_table_nests_inside_a_cell_and_is_removed_again() { + let (space, before) = page_by_title(TREES, "Delete cell subtree"); + let mut after = before.clone(); + let template = table_mut(&mut after).rows[0].cells[0].paragraphs[0].clone(); + let cell = |text: &str| TableCell { + id: new_id().unwrap(), + layout: Default::default(), + indents: Vec::new(), + shading: None, + paragraphs: vec![cell_paragraph(&template, text)], + unsupported: Vec::new(), + }; + let nested = Table { + id: new_id().unwrap(), + columns: vec![TableColumn { + width: 72.0, + locked: true, + }], + rows: vec![ + TableRow { + id: new_id().unwrap(), + cells: vec![cell("Inner one")], + }, + TableRow { + id: new_id().unwrap(), + cells: vec![cell("Inner two")], + }, + ], + borders: Some(true), + layout: Default::default(), + tags: Vec::new(), + }; + let mut holder = cell_paragraph(&template, ""); + holder.content = ParagraphContent::Table(nested); + holder.format = Format::default(); + let holder_id = holder.id; + table_mut(&mut after).rows[0].cells[1] + .paragraphs + .push(holder); + let written = PreparedEdit::page(TREES, space, &after, AUTHOR).unwrap(); + let stored = assert_same(written.as_bytes(), space, &after); + if let Some(directory) = std::env::var_os("ONESTORE_NESTED_TABLE_EXPORT") { + let directory = std::path::PathBuf::from(directory); + std::fs::create_dir(&directory).unwrap(); + std::fs::write(directory.join("synthetic.one"), written.as_bytes()).unwrap(); + let written_store = Store::parse(written.as_bytes()).unwrap(); + std::fs::write( + directory.join("Open Notebook.onetoc2"), + onestore::create_table_of_contents( + "Open Notebook.onetoc2", + &[("synthetic.one", written_store.header.file_id)], + ) + .unwrap(), + ) + .unwrap(); + } + let mut removed = stored.clone(); + table_mut(&mut removed).rows[0].cells[1] + .paragraphs + .retain(|paragraph| paragraph.id != holder_id); + let again = PreparedEdit::page(written.as_bytes(), space, &removed, AUTHOR).unwrap(); + assert_eq!(assert_same(again.as_bytes(), space, &removed), before); +} + #[test] fn inconsistent_tables_are_refused() { let (space, before) = page_by_title(TREES, "Delete cell subtree"); diff --git a/tools/test_table_edit.py b/tools/test_table_edit.py index 12305d45cde9b4fa729a8e4cbe9b154042d685b7..8d6af03f2050ad113b65ef0eaeea82b01e43d36f 100644 --- a/tools/test_table_edit.py +++ b/tools/test_table_edit.py @@ -53,6 +53,12 @@ class TableEditTest(unittest.TestCase): self.assertEqual(rows[0][2], ['Third column']) self.assertEqual(rows[1], [['Second row'], ['Second row, second column'], ['Second row, third column']]) + def test_a_table_nested_in_a_native_cell_renders_natively(self): + self.check('nested') + outer, inner = tables(FIXTURE / 'nested/cold/read', 'Delete cell subtree') + self.assertEqual(inner, ('true', [('72.0', 'true')], [[['Inner one']], [['Inner two']]])) + self.assertEqual(outer[2][0][1], ['Other cell', 'Inner one', 'Inner two']) + if __name__ == '__main__': unittest.main() -- 2.54.0