1param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost)
2Set-StrictMode -Version Latest
3$ErrorActionPreference = 'Stop'
4& "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost
5$histories = Get-Content (Join-Path $Root 'notebook\histories.json') -Raw -Encoding UTF8 | ConvertFrom-Json
6$inputs = Join-Path $Root 'inputs'
7New-Item -ItemType Directory -Path $inputs | Out-Null
8$app = New-Object -ComObject OneNote.Application
9$notebookId = ''
10$namespace = 'http://schemas.microsoft.com/office/onenote/2010/onenote'
11try {
12 $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebookId, 0)
13 $sectionId = ''
14 $app.OpenHierarchy('synthetic.one', $notebookId, [ref]$sectionId, 0)
15 foreach ($history in $histories) {
16 $pageId = ''
17 $app.CreateNewPage($sectionId, [ref]$pageId, 0)
18 $state = $history.initial
19 $paragraphs = New-Object Collections.ArrayList
20 foreach ($paragraph in $state.paragraphs) { [void]$paragraphs.Add($paragraph) }
21 $ids = @{}
22 $outlineId = ''
23 for ($step = -1; $step -lt $history.operations.Count; $step++) {
24 if ($step -ge 0) {
25 $op = $history.operations[$step]
26 switch ($op.kind) {
27 'insert' { $paragraphs.Insert($op.index, $op.paragraph) }
28 'delete' { $paragraphs.RemoveAt($op.index) }
29 'move' { $paragraph = $paragraphs[$op.index]; $paragraphs.RemoveAt($op.index); $paragraphs.Insert($op.to, $paragraph) }
30 'position' { $state.x = $op.x; $state.y = $op.y }
31 'cell' { $state.table[$op.row][$op.column] = $op.text }
32 default { $property = $op.kind; $paragraphs[$op.index].$property = $op.value }
33 }
34 }
35 $xml = '<one:Page xmlns:one="' + $namespace + '" ID="' + $pageId + '"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="History checkbox"/><one:Title><one:OE><one:T>' + $history.title + '</one:T></one:OE></one:Title><one:Outline'
36 if ($outlineId) { $xml += ' objectID="' + $outlineId + '"' }
37 $xml += '><one:Position x="' + $state.x + '" y="' + $state.y + '"/><one:Size width="480" height="100"/><one:OEChildren>'
38 foreach ($paragraph in $paragraphs) {
39 $xml += '<one:OE'
40 if ($ids.ContainsKey($paragraph.id)) { $xml += ' objectID="' + $ids[$paragraph.id] + '"' }
41 $xml += '>'
42 if ($paragraph.tag) { $xml += '<one:Tag index="0" completed="false" disabled="false" creationDate="2020-01-02T03:04:05.000Z"/>' }
43 $decoration = @()
44 if ($paragraph.underline) { $decoration += 'underline' }
45 if ($paragraph.strike) { $decoration += 'line-through' }
46 if ($decoration.Count -eq 0) { $decoration = @('none') }
47 $style = 'font-family:Calibri;font-size:' + $paragraph.font_size + 'pt;font-weight:' + $(if ($paragraph.bold) {'bold'} else {'normal'}) + ';font-style:' + $(if ($paragraph.italic) {'italic'} else {'normal'}) + ';text-decoration:' + ($decoration -join ' ') + ';color:' + $paragraph.color + ';background:' + $paragraph.highlight
48 $html = '<span style="' + $style + '">' + [Security.SecurityElement]::Escape($paragraph.text) + '</span>'
49 if ($paragraph.link) { $html = '<a href="' + [Security.SecurityElement]::Escape($paragraph.link) + '">' + $html + '</a>' }
50 $xml += '<one:T><![CDATA[' + $html + ']]></one:T></one:OE>'
51 }
52 $xml += '<one:OE><one:Table bordersVisible="true"><one:Columns><one:Column index="0" width="180" isLocked="true"/><one:Column index="1" width="180" isLocked="true"/></one:Columns>'
53 foreach ($row in $state.table) {
54 $xml += '<one:Row>'
55 foreach ($cell in $row) { $xml += '<one:Cell><one:OEChildren><one:OE><one:T><![CDATA[' + [Security.SecurityElement]::Escape($cell) + ']]></one:T></one:OE></one:OEChildren></one:Cell>' }
56 $xml += '</one:Row>'
57 }
58 $xml += '</one:Table></one:OE></one:OEChildren></one:Outline></one:Page>'
59 [IO.File]::WriteAllText((Join-Path $inputs ('{0:d6}-{1:d2}.xml' -f $history.seed, ($step + 1))), $xml, [Text.Encoding]::UTF8)
60 $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $true)
61 $content = ''
62 $app.GetPageContent($pageId, [ref]$content, 3, 1)
63 [xml]$document = $content
64 $manager = New-Object Xml.XmlNamespaceManager($document.NameTable)
65 $manager.AddNamespace('one', $namespace)
66 $outline = $document.SelectSingleNode('/one:Page/one:Outline', $manager)
67 $outlineId = $outline.GetAttribute('objectID')
68 $objects = @($outline.SelectNodes('one:OEChildren/one:OE[one:T]', $manager))
69 if ($objects.Count -ne $paragraphs.Count) { throw "Native paragraph count changed at seed $($history.seed), step $step." }
70 for ($index = 0; $index -lt $paragraphs.Count; $index++) { $ids[$paragraphs[$index].id] = $objects[$index].GetAttribute('objectID') }
71 $log = @{ seed = $history.seed; step = $step; page = $pageId; outline = $outlineId } | ConvertTo-Json -Compress
72 [IO.File]::AppendAllText((Join-Path $Root 'operations.jsonl'), $log + [Environment]::NewLine, [Text.Encoding]::UTF8)
73 }
74 }
75 $app.SyncHierarchy($notebookId)
76 $app.CloseNotebook($notebookId, $false)
77 $notebookId = ''
78} finally {
79 if ($notebookId) { $app.CloseNotebook($notebookId, $false) }
80 [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app)
81 [GC]::Collect()
82 [GC]::WaitForPendingFinalizers()
83}