| 1 | #!/usr/bin/env python3 |
| 2 | """Build synthetic OneNote pages for tools/native_runner.py --author tools/native/pages.ps1.""" |
| 3 | import argparse |
| 4 | import html |
| 5 | import json |
| 6 | import math |
| 7 | from pathlib import Path |
| 8 | import xml.etree.ElementTree as ET |
| 9 | |
| 10 | NS = 'http://schemas.microsoft.com/office/onenote/2010/onenote' |
| 11 | ET.register_namespace('one', NS) |
| 12 | |
| 13 | |
| 14 | def build(cases): |
| 15 | pages = [] |
| 16 | seen = set() |
| 17 | for case in cases: |
| 18 | if case['id'] in seen: |
| 19 | raise ValueError('Duplicate case identity') |
| 20 | seen.add(case['id']) |
| 21 | if not math.isfinite(case['width']) or case['width'] <= 0 or not case['runs']: |
| 22 | raise ValueError('Expected positive width and at least one run') |
| 23 | page = ET.Element(f'{{{NS}}}Page', {'name': case['id']}) |
| 24 | title = ET.SubElement(page, f'{{{NS}}}Title') |
| 25 | title_oe = ET.SubElement(title, f'{{{NS}}}OE') |
| 26 | ET.SubElement(title_oe, f'{{{NS}}}T').text = case['id'] |
| 27 | outline = ET.SubElement(page, f'{{{NS}}}Outline') |
| 28 | ET.SubElement(outline, f'{{{NS}}}Position', {'x': '36', 'y': '90', 'z': '0'}) |
| 29 | ET.SubElement(outline, f'{{{NS}}}Size', {'width': str(case['width']), 'height': '20', 'isSetByUser': 'true'}) |
| 30 | children = ET.SubElement(outline, f'{{{NS}}}OEChildren') |
| 31 | fragments = [] |
| 32 | styles = [] |
| 33 | for run in case['runs']: |
| 34 | if not math.isfinite(run['size']) or run['size'] <= 0: |
| 35 | raise ValueError('Expected positive font size') |
| 36 | style = (f"font-family:{run['font']};font-size:{run['size']}pt;" |
| 37 | f"font-weight:{'bold' if run['bold'] else 'normal'};" |
| 38 | f"font-style:{'italic' if run['italic'] else 'normal'}") |
| 39 | styles.append(style) |
| 40 | fragments.append(f'<span style="{html.escape(style, quote=True)}">{html.escape(run["text"])}</span>') |
| 41 | oe = ET.SubElement(children, f'{{{NS}}}OE', {'style': styles[0]}) |
| 42 | ET.SubElement(oe, f'{{{NS}}}T').text = ''.join(fragments) |
| 43 | pages.append({'section': 'Layout.one', 'xml': ET.tostring(page, encoding='unicode')}) |
| 44 | return {'sections': ['Layout.one'], 'pages': pages} |
| 45 | |
| 46 | |
| 47 | if __name__ == '__main__': |
| 48 | parser = argparse.ArgumentParser(description=__doc__) |
| 49 | parser.add_argument('cases', type=Path) |
| 50 | parser.add_argument('destination', type=Path) |
| 51 | args = parser.parse_args() |
| 52 | fixture = build(json.loads(args.cases.read_text())) |
| 53 | args.destination.mkdir(parents=True, exist_ok=False) |
| 54 | (args.destination / 'fixture.json').write_text(json.dumps(fixture, ensure_ascii=False, indent=2) + '\n') |