"""Render README showcases and the full encoded character set.""" import hashlib import json from pathlib import Path import aggdraw from fontTools.pens.basePen import BasePen from fontTools.pens.boundsPen import BoundsPen from fontTools.pens.transformPen import TransformPen from fontTools.ttLib import TTFont from PIL import Image, ImageDraw, ImageFont ROOT = Path(__file__).resolve().parent.parent CODE = '''use std::collections::BTreeMap; fn parse_env(input: &str) -> BTreeMap<&str, &str> { input.lines() .map(str::trim) .filter(|line| !line.starts_with('#')) .filter_map(|line| line.split_once('=')) .map(|(key, value)| (key.trim(), value.trim())) .collect() } let env = parse_env("LANG=de_DE.UTF-8\\nPORT=8080"); assert_eq!(env.get("PORT"), Some(&"8080"));''' STRESS = 'minimum / maximum / memory / window / www.example.org' BACKGROUND, INK, MUTED, RULE, ACCENT = '#ffffff', '#000000', '#6f6f6f', '#d7d7d7', '#595959' MARGIN = 64 class OutlinePen(BasePen): def __init__(self, glyphs): super().__init__(glyphs) self.path = aggdraw.Path() def _moveTo(self, p): self.path.moveto(*p) def _lineTo(self, p): self.path.lineto(*p) def _curveToOne(self, a, b, c): self.path.curveto(*a, *b, *c) def _closePath(self): self.path.close() def code_mask(font, rows, size): """Rasterize (text, em, baseline[, x]) rows without shaping.""" supersample = 4 glyphs, cmap = font.getGlyphSet(), font.getBestCmap() mask = Image.new('L', tuple(v * supersample for v in size)) canvas = aggdraw.Draw(mask) for line, em, baseline, *position in rows: scale = em / font['head'].unitsPerEm x = position[0] if position else 38.0 pen = OutlinePen(glyphs) for char in line: name = cmap[ord(char)] glyphs[name].draw(TransformPen(pen, (scale * supersample, 0, 0, -scale * supersample, x * supersample, baseline * supersample))) x += font['hmtx'][name][0] * scale assert x < size[0] - 30, line canvas.path(pen.path, aggdraw.Brush('white')) canvas.flush() return mask.resize(size, Image.Resampling.LANCZOS) def showcase(font, style, weight): panel = Image.new('RGB', (1400, 1008), BACKGROUND) draw = ImageDraw.Draw(panel) path = ROOT / 'build/TTF' / f'iocgnMono-{style}.ttf' label = ImageFont.truetype(str(path), size=20) size_label = ImageFont.truetype(str(path), size=16) draw.line((MARGIN, 240, panel.width - MARGIN, 240), fill=RULE, width=2) draw.text((MARGIN, 264), '01: code', font=label, fill=MUTED) draw.text((1080, 264), '02: distinct forms', font=label, fill=MUTED) draw.line((1032, 264, 1032, 852), fill=RULE, width=2) draw.text((1080, 500), '03: extended latin', font=label, fill=MUTED) draw.text((1080, 676), '04: sizes', font=label, fill=MUTED) for em, baseline in [(16, 742), (20, 786), (28, 840)]: draw.text((1080, baseline), f'{em}', font=size_label, fill=MUTED, anchor='ls') draw.line((MARGIN, 876, panel.width - MARGIN, 876), fill=RULE, width=2) draw.text((MARGIN, 898), '05: samples', font=label, fill=MUTED) rows = [('iocgn Mono', 112, 144, MARGIN), ('Aa Bb Cc / 0123456789', 42, 206, MARGIN)] rows += [(line, 30, 332 + i * 42, MARGIN) for i, line in enumerate(CODE.splitlines())] rows += [(line, 48, 356 + i * 54, 1080) for i, line in enumerate(('0O 1Il|', '8B 6G 9g', 'aopq mWw'))] rows += [('ÀÉÎÕÜ ß æ œ', 36, 588, 1080), ('āčę ł ñ ø ž', 36, 640, 1080)] rows += [(text, em, baseline, 1128) for text, em, baseline in [('let port = 8080;', 16, 742), ('path.join("src")', 20, 786), ('Some(&"UTF-8")', 28, 840)]] rows += [(STRESS, 24, 966, MARGIN)] panel.paste(INK, (0, 0), code_mask(font, rows, panel.size)) return panel def character_sheet(fonts, extended=False): coverage = sorted(fonts[0].getBestCmap()) assert all(sorted(font.getBestCmap()) == coverage for font in fonts) coverage = [cp for cp in coverage if (cp >= 0x180) == extended] blocks = [('ascii', 0x20, 0x7F), ('latin-1 supplement', 0xA0, 0x100), ('latin extended-a', 0x100, 0x180), ('combining accents', 0x300, 0x370), ('punctuation', 0x2000, 0x2070), ('arrows', 0x2190, 0x2200), ('mathematical symbols', 0x2200, 0x2300), ('box drawing', 0x2500, 0x2580), ('block elements', 0x2580, 0x25A0), ('geometric indicators', 0x25A0, 0x2600), ('checkboxes and stars', 0x2600, 0x2700), ('checks, crosses and navigation', 0x2700, 0x2800), ('braille patterns', 0x2800, 0x2900), ('replacement character', 0xFFFD, 0xFFFE)] sections = [(title, points) for title, start, end in blocks if (points := [cp for cp in coverage if start <= cp < end])] assert sorted(cp for _, points in sections for cp in points) == coverage columns, cell_width, cell_height = 16, 92, 108 height = 320 + sum(72 + ((len(points) + columns - 1) // columns) * cell_height for _, points in sections) panel = Image.new('RGB', (1600, height), BACKGROUND) draw = ImageDraw.Draw(panel) path = ROOT / 'build/TTF/iocgnMono-Regular.ttf' label = ImageFont.truetype(str(path), size=32) small = ImageFont.truetype(str(path), size=24) title = 'other characters' if extended else 'ascii + latin-1 + latin extended-a' draw.text((MARGIN, 48), f'iocgn mono / {title}', font=label, fill=ACCENT, anchor='lt') draw.text((MARGIN, 230), 'Regular + Bold', font=label, fill=MUTED) sample = '→✓□ / ⣿' if extended else 'Aa / Àæ / Āčę' rows = [[(sample, 96, 192, MARGIN)], []] sources = [(font.getGlyphSet(), font.getBestCmap(), 48 / font['head'].unitsPerEm) for font in fonts] y = 284 for title, points in sections: draw.line((MARGIN, y, 1536, y), fill=RULE, width=2) draw.text((MARGIN, y + 20), title, font=label, fill=ACCENT) y += 72 for i, cp in enumerate(points): left, top = MARGIN + (i % columns) * cell_width, y + (i // columns) * cell_height draw.text((left + cell_width / 2, top + 78), f'{cp:04X}', font=small, fill=MUTED, anchor='mt') for style, (glyphs, cmap, scale) in enumerate(sources): pen = BoundsPen(glyphs) glyphs[cmap[cp]].draw(pen) center = left + (27 if style == 0 else 65) if pen.bounds: # Center the ink, including zero-advance marks, rather than the advance cell. x = center - (pen.bounds[0] + pen.bounds[2]) / 2 * scale rows[style].append((chr(cp), 48, top + 58, x)) else: assert cp in (0x20, 0xA0, 0x2800), f'Unexpected empty glyph: U+{cp:04X}' draw.text((center, top + 40), {0x20: 'SP', 0xA0: 'NB', 0x2800: 'BL'}[cp], font=small, fill=MUTED, anchor='mm') y += ((len(points) + columns - 1) // columns) * cell_height for font, glyph_rows in zip(fonts, rows): panel.paste(INK, (0, 0), code_mask(font, glyph_rows, panel.size)) legend = 'BL = blank braille' if extended else 'SP = space / NB = non-breaking space' draw.text((MARGIN, height - 42), legend, font=small, fill=MUTED) return panel, coverage def main(): output = ROOT / 'demos' output.mkdir(exist_ok=True) manifest = {'rendering': 'unhinted vectors, 4x supersampling; fixed advances; no kerning or ligatures', 'em_pixels': [112, 48, 42, 36, 30, 28, 24, 20, 16], 'renderer_sha256': hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), 'fonts': {}} with TTFont(ROOT / 'build/TTF/iocgnMono-Regular.ttf') as regular, \ TTFont(ROOT / 'build/TTF/iocgnMono-Bold.ttf') as bold: for font, style, weight in [(regular, 'Regular', 400), (bold, 'Bold', 700)]: path = ROOT / 'build/TTF' / f'iocgnMono-{style}.ttf' manifest['fonts'][path.name] = hashlib.sha256(path.read_bytes()).hexdigest() showcase(font, style, weight).save(output / f'{style.lower()}.png') manifest['character_sheets'] = {} for extended, filename in ((False, 'characters.png'), (True, 'characters-extended.png')): sheet, coverage = character_sheet((regular, bold), extended=extended) sheet.save(output / filename) manifest['character_sheets'][filename] = { 'em_pixels': [96, 48], 'codepoints': coverage, 'styles': ['Regular', 'Bold'], 'combining_marks': 'isolated outlines'} (ROOT / 'build/demo-manifest.json').write_text(json.dumps(manifest, indent=2) + '\n') print('Rendered demos/regular.png, demos/bold.png, demos/characters.png and demos/characters-extended.png.') if __name__ == '__main__': main()