#!/usr/bin/env python3 """Requested provisional David layout only; no production printing or finish masks.""" from pathlib import Path import hashlib,json,os,subprocess from xml.sax.saxutils import escape import numpy as np from PIL import Image,ImageDraw P=Path(__file__).resolve().parent R=next(p for p in P.parents if (p/'docs/card-workspace.md').is_file()) ART=P.parents[1]/'source/art-master.png' sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() original=sha(ART) fonts=json.loads((R/'fonts/manifest.json').read_text()) for name in ['P052-Bold.otf','SanctificationP052-Medium.otf']: assert sha(R/'fonts'/name)==fonts['files'][name]['sha256'] (P/'fonts.conf').write_text(f'{R/"fonts"}{P/"font-cache"}') env=dict(os.environ,FONTCONFIG_FILE=str(P/'fonts.conf')) def ink(*args):return subprocess.check_output(['inkscape',*map(str,args)],env=env,text=True,stderr=subprocess.PIPE) def render(src,dst):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800') # Hand-authored silhouette of the ENTIRE connected drape, in native 1060 x 1484 coordinates. # Keep whole cloth pixels and internal lead seams, with no color-based alpha or hole repair. head='' path="M 244 930 L 333 930 C 351 988 364 1031 374 1081 C 383 1122 386 1168 392 1206 C 399 1248 403 1293 401 1327 Q 401 1341 392 1346 L 272 1346 C 275 1370 278 1390 282 1402 L 292 1484 L -5 1484 C 27 1398 52 1338 93 1302 C 134 1277 194 1254 239 1237 C 218 1240 198 1247 183 1254 C 208 1205 232 1159 251 1106 C 272 1047 260 985 244 930 Z" scale=2000/1060 clip=f'' fragment_svg=P/'mantle-overlap-preview.svg' fragment_svg.write_text(head+clip+'') render(fragment_svg,P/'mantle-overlap-preview.png') alpha=np.asarray(Image.open(P/'mantle-overlap-preview.png').convert('RGBA').getchannel('A')) (P/'mantle-contour.json').write_text(json.dumps({'method':'Hand-authored continuous SVG silhouette; source cloth RGB unchanged','nativeCanvas':[1060,1484],'path':path,'uniformScale':scale,'component':'Whole connected red drape, including the plane right of the curled front fold and its true lower edge against the stone ledge'},indent=2)+'\n') rows=[('title','DAVID',1490,255,138,'P052',700)] verses=['“I have found David','the son of Jesse, a man','after My own heart,','who will do all My will.”'] for i,t in enumerate(verses):rows.append((f'verse-{i}',t,1320,2306+i*85,80,'Sanctification P052',500)) rows.append(('reference','Acts 13:22 • NKJV',1320,2660,57,'Sanctification P052',500)) for ident,text,x,y,size,family,weight in rows: font=R/'fonts'/('P052-Bold.otf' if weight==700 else 'SanctificationP052-Medium.otf') charset=subprocess.check_output(['fc-query','--format','%{charset}',str(font)],text=True) coverage=set() for span in charset.split(): bounds=[int(v,16) for v in span.split('-')];coverage.update(range(bounds[0],bounds[-1]+1)) assert all(ord(c) in coverage for c in text),ident texts=''.join(f'{escape(text)}' for ident,text,x,y,size,family,weight in rows) head='' (P/'text-preview.svg').write_text(head+texts+'');render(P/'text-preview.svg',P/'text-preview.png') # Simple SVG geometry is intentionally undecorated until layout review. base='' frame='' panels='' (P/'backing-review.svg').write_text(head+panels+'');render(P/'backing-review.svg',P/'backing-review.png') backing_alpha=np.asarray(Image.open(P/'backing-review.png').convert('RGBA').getchannel('A')) overlap='' for name,fg in [('composed-preview',overlap),('without-overlap', '')]: src=P/(name+'.svg');src.write_text(head+base+frame+panels+fg+texts+'');render(src,P/(name+'-2000.png')) image=Image.open(P/(name+'-2000.png')).convert('RGB');image.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png')) # Measure each independently rendered line so no clipping can hide an overlap. checks=[] for ident,text,x,y,size,family,weight in rows: node=next(v for v in texts.split('') if f'id="{ident}"' in v)+'' src=P/(ident+'-ink.svg');src.write_text(head+node+'');dst=P/(ident+'-ink.png');render(src,dst);bounds=Image.open(dst).convert('RGBA').getchannel('A').getbbox();assert bounds box=(1098,108,1882,332) if ident=='title' else (410,2240,1910,2700) assert bounds[0]>=box[0] and bounds[1]>=box[1] and bounds[2]<=box[2] and bounds[3]<=box[3],(ident,bounds,box) inkalpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));assert not np.any((inkalpha>0)&(alpha>0)),ident assert np.all(backing_alpha[inkalpha>0]==255),ident checks.append({'id':ident,'text':text,'font':family,'weight':weight,'size':size,'inkBounds':list(bounds),'safeBox':list(box),'mantleIntersectionPixels':0,'glyphPixelsOutsideOpaqueBacking':0,'glyphCoverageVerified':True}) board=Image.new('RGB',(1000,750),'#11161c');d=ImageDraw.Draw(board) for i,(label,path) in enumerate([('Simple backing, no overlap',P/'without-overlap-1000.png'),('Provisional mantle overlap',P/'composed-preview-1000.png')]): d.text((i*500+12,12),label,fill='#f4ead4');im=Image.open(path);im.thumbnail((480,672));board.paste(im,(i*500+10,45)) board.save(P/'overlap-comparison.png') assert sha(ART)==original report={'status':'passed','scope':'Provisional composed layout preview only','artSHA256':original,'sourceArtworkUnchanged':True,'canvas':[2000,2800],'titleBacking':[1080,90,820,260],'verseBacking':[500,2200,1440,540],'palette':{'backing':'#f4ead4','text':'#263f50','rule':'#806438'},'fontManifestSHA256':sha(R/'fonts/manifest.json'),'textChecks':checks,'overlap':{'method':'Hand-authored continuous vector contour of the entire connected red mantle drape; same-canvas source fragment.','contour':'mantle-contour.json','note':'Entire red cloth plane included; natural right silhouette against blue garment and lower ledge retained. Provisional contour pending aesthetic review.','letteringIntersectionPixels':0},'notPerformed':['Decorative asset generation','Final printing exports','Finish or glyph-protection masks','Harness fixture installation','GPU/moving-light validation']} (P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n');print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(R)),'inkChecks':checks},indent=2)) # The full cloth plane, not merely a corridor, must remain visible. art_array=np.asarray(Image.open(ART).convert('RGB')) fragment=np.asarray(Image.open(P/'mantle-overlap-preview.png').convert('RGBA')) assert np.array_equal(fragment[:,:,:3][alpha==255],art_array[alpha==255]) assert (P/'text-preview.png').read_bytes()==(P.parent/'layout-preview-v2/text-preview.png').read_bytes() board=Image.new('RGB',(1500,750),'#11161c');d=ImageDraw.Draw(board) for i,(label,f) in enumerate([('Previous Astra: incomplete cloth',P.parent/'layout-preview-astra/composed-preview-1000.png'),('Full drape over original backing',P/'composed-preview-1000.png'),('Source art',ART)]): d.text((i*500+12,12),label,fill='#f4ead4');im=Image.open(f);im.thumbnail((480,672));board.paste(im,(i*500+10,45)) board.save(P/'before-after.png') board=Image.new('RGB',(1500,710),'#11161c');d=ImageDraw.Draw(board) for i,(label,f) in enumerate([('Previous Astra',P.parent/'layout-preview-astra/composed-preview-2000.png'),('Full connected cloth',P/'composed-preview-2000.png'),('Source artwork',ART)]): d.text((i*500+12,12),label,fill='#f4ead4');im=Image.open(f).crop((100,1970,980,2800));im.thumbnail((480,645));board.paste(im,(i*500+10,45)) board.save(P/'junction-comparison.png') composed=np.asarray(Image.open(P/'composed-preview-2000.png').convert('RGB')) points=[(335,1150),(350,1200),(370,1260),(375,1310)] for x,y in points: xx,yy=round(x*scale),round(y*scale) assert np.array_equal(composed[yy,xx],art_array[yy,xx]),(x,y) # A broad interior swath explicitly covers the formerly missing right-hand cloth plane. plane=Image.new('L',(2000,2800));ImageDraw.Draw(plane).polygon([(round(x*scale),round(y*scale)) for x,y in [(320,1160),(370,1160),(382,1220),(390,1300),(383,1330),(295,1330),(320,1290)]],fill=255) plane_pixels=np.asarray(plane)>0 assert np.array_equal(composed[plane_pixels],art_array[plane_pixels]) report.update({'sourceFragmentOpaquePixelsMatch':True,'letteringPNGMatchesV2':True,'glyphCoverageVerified':True,'glyphPixelsOutsideOpaqueBacking':0,'formerlyMissingClothPlane':{'nativeInteriorPolygon':[(320,1160),(370,1160),(382,1220),(390,1300),(383,1330),(295,1330),(320,1290)],'preservedSourcePixels':int(plane_pixels.sum()),'spotChecksNative':points,'assessmentBasis':'Full enlarged silhouette compared visually with source; broad interior pixel check only supports that review.'},'staticReview':{'status':'provisional','assessment':'The entire red drape is retained from above the backing, down its true right boundary beside the blue garment, across its dark lower boundary at the stone ledge, and continuously into the front hanging fold. No cream wedge cuts the internal red cloth plane. The prior front-fold-only contour and swept backing are replaced.','limitation':'The source itself has a near-horizontal lower drape edge at the stone ledge. This natural source edge remains visible over the backing; this is a provisional silhouette, not an approved final layer.'}}) (P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n') print(json.dumps({'preservedClothPlanePixels':int(plane_pixels.sum()),'glyphPixelsOutsideOpaqueBacking':0,'letteringPNGMatchesV2':True},indent=2))