1"""Type hyperlinks into OneNote 2010 on a Rust-written page and capture what it stores: URLs
2it links as they are typed, links made with the Link dialog (Ctrl+K), typing after a link and
3Remove Link. The oracle for the canvas editor's link editing.
4
5Usage: native_links.py NOTEBOOK_DIR OUTPUT_DIR
6"""
7import base64
8import json
9import sys
10from pathlib import Path
11import xml.etree.ElementTree as ET
12
13sys.path.insert(0, str(Path(__file__).resolve().parent))
14import native_runner as runner
15
16windows = runner.windows
17
18# Each typed URL sits between a word and a letter, one per paragraph; a space or Enter ends it.
19TYPED = [
20 'www.example.com',
21 'http://example.org/a?b=1',
22 'https://a.example/p.',
23 '(http://b.example/q)',
24 'ftp://c.example/f',
25 'mailto:me@example.com',
26 'me@example.com',
27 'example.com',
28 'onenote:#Page&section-id={x}',
29 'file://c:/windows',
30 '\\\\server\\share\\f',
31 'news:comp.lang',
32 'www.d.example,',
33 'HTTP://E.EXAMPLE/UP',
34 'https://f.example/a_b-c~d%20e#frag',
35]
36
37
38def text(value):
39 return 'SendText("%s")' % value.replace('"', '""')
40
41
42def interaction(name, output):
43 lines = []
44 for url in TYPED:
45 lines += [text('t ' + url), 'Send(" ")', text('z'), 'Send("{Enter}")', 'Sleep(300)']
46 # A URL ended by Enter, then a letter typed straight after the link it made.
47 lines += [text('see http://g.example/end'), 'Send("{Enter}")', 'Send("{Up}{End}")', text('Z'),
48 'Send("{Down}{End}")', 'Sleep(300)']
49 # The Link dialog on the word at the caret: its text is the word, the address as typed.
50 lines += [text('plain words here'), 'Send("^{Left}")', 'Send("^k")', 'Sleep(1500)',
51 text('example.net/x'), 'Send("{Enter}")', 'Sleep(800)', 'Send("{End}")', text('Q'),
52 'Send("{Enter}")', 'Sleep(300)']
53 # The Link dialog on a selection.
54 lines += [text('select me please'), 'Send("+^{Left}+^{Left}")', 'Send("^k")', 'Sleep(1500)',
55 text('https://example.com/selected'), 'Send("{Enter}")', 'Sleep(800)',
56 'Send("{End}{Enter}")', 'Sleep(300)']
57 # The Link dialog with no text: the address becomes the text; typing after it is plain.
58 lines += ['Send("^k")', 'Sleep(1500)', 'Send("!e")', 'Sleep(300)',
59 text('https://example.com/only'), 'Send("{Enter}")', 'Sleep(800)', text(' tail'),
60 'Send("{Enter}")', 'Sleep(300)']
61 # Remove Link from the context menu (Shift+F10, R) with the caret inside a typed link.
62 lines += [text('gone ftp://h.example/f'), 'Send(" ")', text('z'), 'Send("{Home}^{Right}^{Right}")',
63 'Send("+{F10}")', 'Sleep(900)', 'Send("r")', 'Sleep(800)']
64 page = ET.parse(sorted((output / 'read').glob('page-*.xml'))[0]).getroot().attrib['ID']
65 script = '''ComObject("OneNote.Application").NavigateTo("PAGE_ID", "", false)
66WinWait("ahk_exe ONENOTE.EXE", , 30)
67WinActivate("ahk_exe ONENOTE.EXE")
68WinWaitActive("ahk_exe ONENOTE.EXE", , 30)
69WinMaximize("ahk_exe ONENOTE.EXE")
70Sleep(3000)
71CoordMode("Mouse", "Screen")
72Click(200, 140)
73Sleep(500)
74Send("{End}")
75Sleep(300)
76Send("{Enter}")
77Sleep(500)
78%s
79Sleep(2000)
80''' % '\n'.join(lines)
81 script = script.replace('PAGE_ID', page)
82 (output / 'links.ahk').write_text(script)
83 result = windows.do_exec(script, target=name, timeout_ms=240000, shot_delay_ms=1500)
84 screenshot = result.pop('png_b64', None)
85 if screenshot:
86 (output / 'links.png').write_bytes(base64.b64decode(screenshot))
87 (output / 'links.json').write_text(json.dumps(result, indent=2))
88 if result.get('error') or result.get('exit') != 0:
89 raise RuntimeError(str(result))
90
91
92if __name__ == '__main__':
93 notebook, output = Path(sys.argv[1]), Path(sys.argv[2])
94 runner.capture(notebook, output, expected_pages=1, collect_notebook=True, interaction=interaction)