| 1 | #!/usr/bin/env python3 |
| 2 | """Create a synthetic native baseline page with independent raster coordinate anchors.""" |
| 3 | import argparse |
| 4 | import base64 |
| 5 | import json |
| 6 | from pathlib import Path |
| 7 | import struct |
| 8 | import xml.etree.ElementTree as ET |
| 9 | import zlib |
| 10 | |
| 11 | from native_fixture import NS |
| 12 | |
| 13 | |
| 14 | def build(): |
| 15 | def chunk(kind, data): |
| 16 | return struct.pack('!I', len(data)) + kind + data + struct.pack('!I', zlib.crc32(kind + data)) |
| 17 | |
| 18 | page = ET.Element(f'{{{NS}}}Page', {'name': 'Baseline anchors'}) |
| 19 | ET.SubElement(page, f'{{{NS}}}QuickStyleDef', { |
| 20 | 'index': '0', 'name': 'p', 'font': 'Calibri', 'fontSize': '11', |
| 21 | 'fontColor': 'automatic', 'highlightColor': 'automatic', 'spaceBefore': '0', 'spaceAfter': '0', |
| 22 | }) |
| 23 | ET.SubElement(page, f'{{{NS}}}QuickStyleDef', { |
| 24 | 'index': '1', 'name': 'h2', 'font': 'Arial', 'fontSize': '11', 'bold': 'false', |
| 25 | 'fontColor': 'automatic', 'highlightColor': 'automatic', 'spaceBefore': '3', 'spaceAfter': '4', |
| 26 | }) |
| 27 | title = ET.SubElement(page, f'{{{NS}}}Title') |
| 28 | oe = ET.SubElement(title, f'{{{NS}}}OE') |
| 29 | ET.SubElement(oe, f'{{{NS}}}T').text = 'Baseline anchors' |
| 30 | for index, (x, y, width, height) in enumerate([ |
| 31 | (20.125, 60.25, 11, 13), (440.5, 355.375, 17, 19), (25.75, 650.875, 23, 29), |
| 32 | ]): |
| 33 | color = bytes(255 if channel == index else 0 for channel in range(3)) |
| 34 | pixels = (b'\x00' + color * (width * 2)) * (height * 2) |
| 35 | png = (b'\x89PNG\r\n\x1a\n' + chunk(b'IHDR', struct.pack('!2I5B', width * 2, height * 2, 8, 2, 0, 0, 0)) |
| 36 | + chunk(b'IDAT', zlib.compress(pixels)) + chunk(b'IEND', b'')) |
| 37 | image = ET.SubElement(page, f'{{{NS}}}Image', {'format': 'png'}) |
| 38 | ET.SubElement(image, f'{{{NS}}}Position', {'x': str(x), 'y': str(y), 'z': str(index)}) |
| 39 | ET.SubElement(image, f'{{{NS}}}Size', {'width': str(width), 'height': str(height), 'isSetByUser': 'true'}) |
| 40 | ET.SubElement(image, f'{{{NS}}}Data').text = base64.b64encode(png).decode() |
| 41 | for index, (y, width, font, size, paragraphs) in enumerate([ |
| 42 | (90, 300, 'Arial', 11, ['Arial eleven HAMBURGEFONTS']), |
| 43 | (150, 300, 'Arial', 16, ['Arial sixteen HAMBURGEFONTS']), |
| 44 | (210, 300, 'Calibri', 11, ['Calibri eleven HAMBURGEFONTS']), |
| 45 | (270, 300, 'Calibri', 17, ['Calibri seventeen HAMBURGEFONTS']), |
| 46 | (330, 110, 'Arial', 11, ['Wrapped HAMBURGEFONTS abcdefghijklmnopqrstuvwxyz 0123456789']), |
| 47 | (420, 300, 'Arial', 11, ['Mixed <span style="font-family:Arial;font-size:16pt">HAMBURGEFONTS</span> small']), |
| 48 | (490, 300, 'Arial', 11, ['Spacing first', '', 'Spacing last']), |
| 49 | (570, 300, 'Calibri', 11, ['Blank first', '', 'Blank last']), |
| 50 | ]): |
| 51 | outline = ET.SubElement(page, f'{{{NS}}}Outline') |
| 52 | ET.SubElement(outline, f'{{{NS}}}Position', {'x': '120', 'y': str(y), 'z': str(index + 3)}) |
| 53 | ET.SubElement(outline, f'{{{NS}}}Size', {'width': str(width), 'height': '20', 'isSetByUser': 'true'}) |
| 54 | children = ET.SubElement(outline, f'{{{NS}}}OEChildren') |
| 55 | for text in paragraphs: |
| 56 | style = f'font-family:{font};font-size:{size}pt' |
| 57 | oe = ET.SubElement(children, f'{{{NS}}}OE', {'style': style}) |
| 58 | if y == 490: |
| 59 | oe.set('quickStyleIndex', '1') |
| 60 | ET.SubElement(oe, f'{{{NS}}}T').text = text |
| 61 | return {'sections': ['Baselines.one'], 'pages': [{'section': 'Baselines.one', 'xml': ET.tostring(page, encoding='unicode')}]} |
| 62 | |
| 63 | |
| 64 | if __name__ == '__main__': |
| 65 | parser = argparse.ArgumentParser(description=__doc__) |
| 66 | parser.add_argument('destination', type=Path) |
| 67 | args = parser.parse_args() |
| 68 | args.destination.mkdir(parents=True, exist_ok=False) |
| 69 | (args.destination / 'fixture.json').write_text(json.dumps(build(), indent=2) + '\n') |