| 1 | param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) |
| 2 | # Pressure ink as OneNote 2010 keeps it: WPF writes strokes with NormalPressure (and one with |
| 3 | # tilt) as ISF, and OneNote takes each through UpdatePageContent into a section of its own. |
| 4 | Set-StrictMode -Version Latest |
| 5 | $ErrorActionPreference = 'Stop' |
| 6 | & "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost |
| 7 | Add-Type -AssemblyName PresentationCore, WindowsBase |
| 8 | $P = [System.Windows.Input.StylusPointProperties] |
| 9 | $U = [System.Windows.Input.StylusPointPropertyUnit] |
| 10 | |
| 11 | function Info($property, $min, $max, $unit, $resolution) { |
| 12 | if ($null -eq $min) { return New-Object System.Windows.Input.StylusPointPropertyInfo($property) } |
| 13 | New-Object System.Windows.Input.StylusPointPropertyInfo($property, $min, $max, $unit, $resolution) |
| 14 | } |
| 15 | |
| 16 | # A stroke through `points` of [x, y, pressure 0..1, extra values...] in WPF units (1/96 inch). |
| 17 | function Stroke($points, $width, $height, [switch]$Ignore, [switch]$Rectangle, $infos) { |
| 18 | if (-not $infos) { $infos = @((Info $P::X), (Info $P::Y), (Info $P::NormalPressure)) } |
| 19 | $description = New-Object System.Windows.Input.StylusPointDescription(,[System.Windows.Input.StylusPointPropertyInfo[]]$infos) |
| 20 | $pressure = $infos[2] |
| 21 | $collection = New-Object System.Windows.Input.StylusPointCollection(,$description) |
| 22 | foreach ($p in $points) { |
| 23 | $extra = [int[]]@($p | Select-Object -Skip 3) |
| 24 | $collection.Add((New-Object System.Windows.Input.StylusPoint($p[0], $p[1], [single]0.5, $description, $extra))) |
| 25 | $point = $collection[$collection.Count - 1] |
| 26 | $point.SetPropertyValue([System.Windows.Input.StylusPointProperties]::NormalPressure, |
| 27 | [int][Math]::Round($pressure.Minimum + $p[2] * ($pressure.Maximum - $pressure.Minimum))) |
| 28 | $collection[$collection.Count - 1] = $point |
| 29 | } |
| 30 | $stroke = New-Object System.Windows.Ink.Stroke -ArgumentList (,$collection) |
| 31 | $stroke.DrawingAttributes.Width = $width |
| 32 | $stroke.DrawingAttributes.Height = $height |
| 33 | $stroke.DrawingAttributes.IgnorePressure = [bool]$Ignore |
| 34 | $stroke.DrawingAttributes.FitToCurve = $false |
| 35 | if ($Rectangle) { $stroke.DrawingAttributes.StylusTip = [System.Windows.Ink.StylusTip]::Rectangle } |
| 36 | $stroke |
| 37 | } |
| 38 | |
| 39 | function Isf($strokes) { |
| 40 | $collection = New-Object System.Windows.Ink.StrokeCollection |
| 41 | foreach ($stroke in $strokes) { $collection.Add([System.Windows.Ink.Stroke]$stroke) } |
| 42 | $stream = New-Object IO.MemoryStream |
| 43 | $collection.Save($stream, $true) |
| 44 | [Convert]::ToBase64String($stream.ToArray()) |
| 45 | } |
| 46 | |
| 47 | function Line($x, $y, $pressure, $count, $step) { |
| 48 | $points = @() |
| 49 | for ($i = 0; $i -lt $count; $i++) { $points += ,@(($x + $i * $step), $y, $pressure) } |
| 50 | ,$points |
| 51 | } |
| 52 | |
| 53 | # "Levels": one drawing of nine 24-point strokes at pressures 0 to 1 in eighths, and one of |
| 54 | # the same levels on a pen reporting 0..255. |
| 55 | $levels = @(); $coarse = @() |
| 56 | $narrow = @((Info $P::X), (Info $P::Y), (Info $P::NormalPressure 0 255 $U::None 1)) |
| 57 | for ($i = 0; $i -le 8; $i++) { |
| 58 | $levels += Stroke (Line 0 ($i * 48) ($i / 8) 5 20) 32 32 |
| 59 | $coarse += Stroke (Line 0 ($i * 48) ($i / 8) 5 20) 32 32 -infos $narrow |
| 60 | } |
| 61 | # "Strokes": a rising ramp, a wave, a pen ignoring the pressure it recorded, a stroke with |
| 62 | # tilt, and a rectangular tip, each a drawing of its own. |
| 63 | $ramp = @(); $wave = @(); $flat = @(); $tilt = @(); $block = @() |
| 64 | for ($i = 0; $i -le 40; $i++) { |
| 65 | $ramp += ,@(($i * 8), 0, ($i / 40)) |
| 66 | $wave += ,@(($i * 8), (20 * [Math]::Sin($i / 4)), (0.5 + 0.5 * [Math]::Sin($i / 5))) |
| 67 | $flat += ,@(($i * 8), 0, ($i / 40)) |
| 68 | $tilt += ,@(($i * 8), 0, (0.2 + $i / 60), (-4500 + $i * 200), 3000) |
| 69 | } |
| 70 | for ($i = 0; $i -le 10; $i++) { $block += ,@(($i * 20), 0, ($i / 10)) } |
| 71 | $tilted = @((Info $P::X), (Info $P::Y), (Info $P::NormalPressure), |
| 72 | (Info $P::XTiltOrientation -9000 9000 $U::Degrees 100), (Info $P::YTiltOrientation -9000 9000 $U::Degrees 100)) |
| 73 | $pages = [ordered]@{ |
| 74 | 'Levels' = @(@(36, 90, (Isf $levels)), @(252, 90, (Isf $coarse))) |
| 75 | 'Strokes' = @( |
| 76 | @(36, 90, (Isf @(Stroke $ramp 8 8))), |
| 77 | @(36, 150, (Isf @(Stroke $wave 4 4))), |
| 78 | @(36, 220, (Isf @(Stroke $flat 8 8 -Ignore))), |
| 79 | @(36, 270, (Isf @(Stroke $tilt 8 8 -infos $tilted))), |
| 80 | @(36, 320, (Isf @(Stroke $block 4 24 -Rectangle)))) |
| 81 | } |
| 82 | |
| 83 | $namespace = 'http://schemas.microsoft.com/office/onenote/2010/onenote' |
| 84 | $app = New-Object -ComObject OneNote.Application |
| 85 | $notebookId = '' |
| 86 | try { |
| 87 | $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebookId, 0) |
| 88 | $sectionId = '' |
| 89 | $app.OpenHierarchy('Pressure.one', $notebookId, [ref]$sectionId, 3) |
| 90 | foreach ($name in $pages.Keys) { |
| 91 | $pageId = '' |
| 92 | $app.CreateNewPage($sectionId, [ref]$pageId, 0) |
| 93 | $drawings = ($pages[$name] | ForEach-Object { |
| 94 | "<one:InkDrawing><one:Position x=`"$($_[0])`" y=`"$($_[1])`"/><one:Data>$($_[2])</one:Data></one:InkDrawing>" |
| 95 | }) -join '' |
| 96 | $xml = "<?xml version=`"1.0`"?><one:Page xmlns:one=`"$namespace`" ID=`"$pageId`"><one:Title><one:OE><one:T><![CDATA[$name]]></one:T></one:OE></one:Title>$drawings</one:Page>" |
| 97 | [IO.File]::WriteAllText((Join-Path $Root "author-$name.xml"), $xml, [Text.Encoding]::UTF8) |
| 98 | $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $false) |
| 99 | } |
| 100 | $app.SyncHierarchy($notebookId) |
| 101 | $app.CloseNotebook($notebookId, $false) |
| 102 | $notebookId = '' |
| 103 | } finally { |
| 104 | if ($notebookId) { $app.CloseNotebook($notebookId, $false) } |
| 105 | [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) |
| 106 | [GC]::Collect() |
| 107 | [GC]::WaitForPendingFinalizers() |
| 108 | } |