#!/usr/bin/env python3 """Four controlled font proofs using retained SOL v2 assembly and validation.""" from pathlib import Path import hashlib, json, subprocess, sys import numpy as np from PIL import Image ROOT = Path(__file__).resolve().parent OUT = ROOT/'output' (OUT/'review').mkdir(parents=True, exist_ok=True) choices = json.loads((ROOT/'font-choices.json').read_text()) def run(*args): subprocess.run([str(x) for x in args], check=True) def pixels(path): return np.asarray(Image.open(path).convert('RGBA')) def pixel_sha(path): return hashlib.sha256(pixels(path).tobytes()).hexdigest() reports = {} for item in choices: folder = ROOT/'variants'/item['slug'] if '--review-only' not in sys.argv: run(sys.executable, folder/'build.py') reports[item['slug']] = json.loads((folder/'output'/f"{item['prefix']}-validation.json").read_text()) # A controlled experiment: exactly the same source inputs and material layers. preserved = {} provenance = json.loads((ROOT/'source/provenance.json').read_text()) for relative in ['source/calling-disciples-SOL-art.png', 'source/calling-disciples-SOL-frame-runtime.png']: hashes = [hashlib.sha256((ROOT/'variants'/i['slug']/relative).read_bytes()).hexdigest() for i in choices] assert len(set(hashes)) == 1 preserved[relative] = {'byteIdenticalAcrossVariants': True, 'sha256': hashes[0]} for suffix in ['art-master', 'frame-runtime', 'backing-normal', 'backing-borderless', 'normal-runtime-overlay']: hashes = [pixel_sha(ROOT/'variants'/i['slug']/'output/layers'/f"{i['prefix']}-{suffix}.png") for i in choices] assert len(set(hashes)) == 1, suffix assert hashes[0] == provenance['preservedLayerDecodedRGBA_SHA256'][suffix], suffix preserved[suffix] = {'pixelIdenticalAcrossVariants': True, 'pixelIdenticalToSOLv2': True, 'decodedRGBA_SHA256': hashes[0]} for item in choices: layout = json.loads((ROOT/'variants'/item['slug']/'layout.json').read_text()) assert layout['fontSizes'] == {'title':112, 'verse':80, 'reference':57} assert layout['backingTreatment']['baseColor'] == '#f4ead4' assert layout['backingTreatment']['palette']['type'] == '#263f50' assert reports[item['slug']]['status'] == 'passed' label_font = ROOT/'variants/noto-sans/fonts/NotoSans-Regular.ttf' for size in [500,1000]: tiles = [] for item in choices: source = ROOT/'variants'/item['slug']/'output/cards'/f"{item['prefix']}-normal-{size}.png" tile = OUT/'review'/f"{item['slug']}-{size}-tile.png" label = 'Helvetica* / Nimbus Sans' if item['slug']=='helvetica' else item['renderedFamily'] run('magick',source,'-background','#ede9df','-gravity','north','-splice',f'0x{size//7}','-font',label_font,'-fill','#263f50','-pointsize',str(size//20),'-gravity','north','-annotate',f'+0+{size//35}',label,tile) tiles.append(tile) run('magick','montage',*tiles,'-tile','4x1','-geometry',f'{size}x{size*7//5+size//7}+18+18','-background','#ede9df',OUT/'review'/f'calling-disciples-SOL-font-comparison-{size}.png') # Real 320px-wide digital view, separately labeled so it is clear what is shown. tiles=[] for item in choices: source=ROOT/'variants'/item['slug']/'output/cards'/f"{item['prefix']}-normal-2000.png" tile=OUT/'review'/f"{item['slug']}-320-tile.png" label='Helvetica* / Nimbus Sans' if item['slug']=='helvetica' else item['renderedFamily'] run('magick',source,'-filter','Lanczos','-resize','320x448','-background','#ede9df','-gravity','north','-splice','0x48','-font',label_font,'-fill','#263f50','-pointsize','16','-gravity','north','-annotate','+0+12',label,tile) tiles.append(tile) run('magick','montage',*tiles,'-tile','4x1','-geometry','320x496+14+14','-background','#ede9df',OUT/'review/calling-disciples-SOL-font-comparison-320.png') # Header and verse crops preserve native size, simplifying stroke/glyph inspection. for section,geometry in [('headers','2000x300+0+60'),('verses','2000x560+0+2180')]: tiles=[] for item in choices: source=ROOT/'variants'/item['slug']/'output/cards'/f"{item['prefix']}-normal-2000.png" tile=OUT/'review'/f"{item['slug']}-{section}.png" run('magick',source,'-crop',geometry,'+repage','-background','#ede9df','-gravity','north','-splice','0x70','-font',label_font,'-fill','#263f50','-pointsize','36','-annotate','+0+16',item['label'],tile) tiles.append(tile) run('magick','montage',*tiles,'-tile','1x4','-geometry','+10+10','-background','#ede9df',OUT/'review'/f'calling-disciples-SOL-font-comparison-{section}.png') report = {'status':'passed','proof':'Controlled font comparison; only font family varies','fontChoices':choices,'palette':{'text':'#263f50','backingBase':'#f4ead4'},'preserved':preserved,'variantValidation':reports,'checks':{'sameFontSizesBaselinesAndLineBreaks':True,'samePaletteAndBackingAcrossVariants':True,'exactReference':'Matthew 4:19 • NKJV','noVisibleCategoryOrSubtitle':True},'limitations':['Helvetica is not installed; its proof explicitly uses pinned Nimbus Sans rather than genuine Helvetica glyphs.','Visual preference and readability require user review; automated glyph fit does not establish optical quality.','No human or moving-light harness approval is claimed.']} (OUT/'validation.json').write_text(json.dumps(report,indent=2)+'\n') print(f'Four-font comparison built and validated: {ROOT}')