Add Legendary Moses card and production guidance
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
# Moses — refined cloud foreground
|
||||
|
||||
This provisional Normal candidate retains the v3 composition and refines the cloud where it crosses the name and verse backings.
|
||||
|
||||
- [Full preview](composed-preview-1000.png)
|
||||
- [500 px preview](composed-preview-500.png)
|
||||
- [V3 / V4 card comparison](v3-v4-comparison.png)
|
||||
- [V3 / V4 enlarged junction comparison](v3-v4-junction-comparison.png)
|
||||
- [V4 interaction details](cloud-interaction-detail.png)
|
||||
- [Editable foreground](foreground-curtain.svg)
|
||||
- [Reproducible builder](build-preview.py)
|
||||
- [Validation](preview-validation.json)
|
||||
|
||||
The prior contour passed through existing lead seams and retained small sky/terrain slivers, producing a clipped edge. V4 traces the cloud with fewer continuous cubic Bézier segments. A consistent eight-master-pixel dark lead perimeter integrates the exposed edges with the stained-glass construction. At the lower end, an authored rounded closure replaces the broken boundary inherited from the source's rocky ground. This foreground adaptation is deliberate; it does not need to retain that original occlusion.
|
||||
|
||||
The cloud interior stays registered to the original illustration. Geometry-heavy outputs render at 4000 × 5600, then downsample to the 2000 × 2800 master in premultiplied RGBA using Lanczos. The glass structure receives no blur. The full source art remains unchanged.
|
||||
|
||||
Exact title, verse and reference are retained. Font hashes, glyph coverage, measured safe bounds, zero foreground/text intersection, and opaque backing under all lettering are checked. The text and frame/backing raster components must match v3 pixel for pixel. Static inspection covers full card and enlarged upper/lower joins.
|
||||
|
||||
Rebuild from the repository with:
|
||||
|
||||
`python3 in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v4/build-preview.py`
|
||||
|
||||
Requires Inkscape, Fontconfig, Pillow and NumPy. This is a provisional review composition only. V3 is preserved. No production layers, printing exports, finish masks, harness fixture, GPU review or promotion are included.
|
||||
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Provisional BP-001 Moses Legendary composition; no production assembly."""
|
||||
from pathlib import Path
|
||||
import hashlib,json,os,subprocess
|
||||
from xml.sax.saxutils import escape
|
||||
import numpy as np
|
||||
from PIL import Image,ImageDraw,ImageFont
|
||||
|
||||
P=Path(__file__).resolve().parent
|
||||
REV=P.parents[1]
|
||||
REPO=next(p for p in P.parents if (p/'docs/card-layer-pipeline.md').is_file())
|
||||
ART=REV/'source/art-master.png'
|
||||
FONTS=REPO/'fonts'
|
||||
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
art_hash=sha(ART)
|
||||
font_manifest=json.loads((FONTS/'manifest.json').read_text())
|
||||
font_files={'title':'P052-Bold.otf','verse':'SanctificationP052-Medium.otf','reference':'SanctificationP052-Medium.otf'}
|
||||
for name in set(font_files.values()):
|
||||
assert sha(FONTS/name)==font_manifest['files'][name]['sha256']
|
||||
(P/'font-cache').mkdir(exist_ok=True)
|
||||
(P/'fonts.conf').write_text(f'<fontconfig><dir>{FONTS}</dir><cachedir>{P/"font-cache"}</cachedir></fontconfig>')
|
||||
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).strip()
|
||||
def render(src,dst):
|
||||
# Supersample only geometry-heavy composite/foreground outputs; text measurements
|
||||
# remain native. LANCZOS uses premultiplied RGBA via Pillow's RGBa path.
|
||||
factor=2 if src.stem in {'foreground-curtain','composed-preview','without-curtain'} else 1
|
||||
ink(src,'--export-type=png',f'--export-filename={dst}',f'--export-width={2000*factor}',f'--export-height={2800*factor}')
|
||||
if factor>1:
|
||||
im=Image.open(dst).convert('RGBA')
|
||||
im.convert('RGBa').resize((2000,2800),Image.Resampling.LANCZOS).convert('RGBA').save(dst)
|
||||
|
||||
def resolve(family,style):return Path(subprocess.check_output(['fc-match','-f','%{file}',f'{family}:style={style}'],env=env,text=True).strip()).resolve()
|
||||
assert resolve('P052','Bold')==(FONTS/'P052-Bold.otf').resolve()
|
||||
assert resolve('Sanctification P052','Medium')==(FONTS/'SanctificationP052-Medium.otf').resolve()
|
||||
|
||||
head='<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2000" height="2800" viewBox="0 0 2000 2800">'
|
||||
defs='''<defs>
|
||||
<linearGradient id="paper" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fbf3df"/><stop offset=".5" stop-color="#f4ead4"/><stop offset="1" stop-color="#e8d4b0"/></linearGradient>
|
||||
<linearGradient id="gold" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f0d47d"/><stop offset=".48" stop-color="#bd8b31"/><stop offset="1" stop-color="#71501e"/></linearGradient>
|
||||
<pattern id="weave" width="44" height="44" patternUnits="userSpaceOnUse"><path d="M0 11H44 M0 33H44 M11 0V44 M33 0V44" stroke="#9d783d" stroke-width="1" opacity=".045"/><path d="M22 7l7 15-7 15-7-15Z" fill="none" stroke="#806438" stroke-width="1" opacity=".07"/></pattern>
|
||||
<pattern id="lapis-band" width="34" height="34" patternUnits="userSpaceOnUse"><path d="M17 2L32 17 17 32 2 17Z" fill="#173e59" stroke="#d7b35a" stroke-width="3"/><circle cx="17" cy="17" r="4" fill="url(#gold)"/></pattern>
|
||||
<clipPath id="curtain-clip"><path d="M-10-10 H770 C734 204 694 395 650 585 C610 770 566 972 522 1170 C474 1388 426 1604 378 1818 C338 2000 302 2190 286 2380 C276 2510 304 2640 412 2810 H-10Z"/></clipPath>
|
||||
</defs>'''
|
||||
base='<image xlink:href="../../source/art-master.png" width="2000" height="2800"/>'
|
||||
frame='''<g id="legendary-frame">
|
||||
<rect x="14" y="14" width="1972" height="2772" rx="34" fill="none" stroke="#2c2115" stroke-width="28"/>
|
||||
<rect x="25" y="25" width="1950" height="2750" rx="27" fill="none" stroke="url(#gold)" stroke-width="14"/>
|
||||
<rect x="43" y="43" width="1914" height="2714" rx="20" fill="none" stroke="#173e59" stroke-width="10"/>
|
||||
<rect x="55" y="55" width="1890" height="2690" rx="15" fill="none" stroke="#d7b35a" stroke-width="4"/>
|
||||
<path d="M68 260V68H260 M1740 68H1932V260 M68 2540V2732H260 M1740 2732H1932V2540" fill="none" stroke="#9b6d26" stroke-width="6"/>
|
||||
<g fill="url(#gold)" stroke="#533810" stroke-width="3"><path d="M68 68l14 14-14 14-14-14Z"/><path d="M1932 68l14 14-14 14-14-14Z"/><path d="M68 2732l14 14-14 14-14-14Z"/><path d="M1932 2732l14 14-14 14-14-14Z"/></g>
|
||||
<rect x="1906" y="430" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/><rect x="1906" y="2115" width="27" height="255" fill="url(#lapis-band)" opacity=".95"/>
|
||||
</g>'''
|
||||
# The panels tuck behind the original tent and the pillar of cloud.
|
||||
# No alteration, retouch, rescaling or regeneration of the selected source.
|
||||
title_path='M700 62 H1900 Q1940 62 1940 102 V342 H700Z'
|
||||
verse_path='M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z'
|
||||
title_inner='<path d="M710 82 H1898 Q1920 82 1920 104 V320 H710" fill="none" stroke="#bb9040" stroke-width="3"/><path d="M1070 301H1870" stroke="#c5a257" stroke-width="2"/>'
|
||||
verse_inner='<path d="M280 2114H1893 Q1920 2114 1920 2141 V2708 Q1920 2736 1892 2736 H280" fill="none" stroke="#bb9040" stroke-width="3"/><path d="M827 2172H1815 M827 2700H1815" stroke="#c5a257" stroke-width="2"/>'
|
||||
def panel(path,inner,ident):return f'<g id="{ident}"><path d="{path}" fill="url(#paper)" stroke="#62421e" stroke-width="9"/><path d="{path}" fill="url(#weave)" stroke="url(#gold)" stroke-width="4"/>{inner}</g>'
|
||||
panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')
|
||||
# Trace the visible source silhouette, including the whole cloud lower lobe.
|
||||
# Upper crown is separately registered; both layers stay at source coordinates.
|
||||
# Cubic contours follow the source lead, not the rough v3 clipping guesses.
|
||||
# The lower closing arc ends above the old terrain occlusion.
|
||||
top_edge='M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369'
|
||||
lower_edge='M736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350'
|
||||
cloud_top=top_edge+' L730 475 H0 V0Z'
|
||||
cloud_low='M0 400H352 V1780H765 C765 1920 748 2010 736 2080 '+lower_edge.removeprefix('M736 2080 ')+' L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z'
|
||||
defs=defs.replace('</defs>',f'<clipPath id="cloud-top"><path d="{cloud_top}"/></clipPath><clipPath id="cloud-low"><path d="{cloud_low}"/></clipPath></defs>')
|
||||
# Quiet fine-gold enclosure: motifs reside in the original curtain and cloud.
|
||||
frame='''<g id="legendary-frame"><rect x="19" y="19" width="1962" height="2762" rx="28" fill="none" stroke="#251c15" stroke-width="21"/><rect x="23" y="23" width="1954" height="2754" rx="23" fill="none" stroke="url(#gold)" stroke-width="11"/><rect x="43" y="43" width="1914" height="2714" rx="15" fill="none" stroke="#e2bf6c" stroke-width="3"/></g>'''
|
||||
|
||||
# A narrow opaque lead edge removes blue/terrain fringe without softening glass.
|
||||
lead=f'<g fill="none" stroke="#2b2116" stroke-width="8" stroke-linejoin="round" stroke-linecap="round"><path d="{top_edge}"/><path d="{lower_edge}"/></g>'
|
||||
curtain='<g id="presence-foreground">'+''.join(f'<g clip-path="url(#{clip})"><image xlink:href="../../source/art-master.png" width="2000" height="2800"/></g>' for clip in ['cloud-top','cloud-low'])+lead+'</g>'
|
||||
(P/'foreground-curtain.svg').write_text(head+defs+curtain+'</svg>');render(P/'foreground-curtain.svg',P/'foreground-curtain.png')
|
||||
fg=Image.open(P/'foreground-curtain.png').convert('RGBA')
|
||||
fa=np.asarray(fg.getchannel('A'))
|
||||
assert fa.max()==255 and fa.min()==0
|
||||
|
||||
rows=[
|
||||
('title','MOSES',1458,244,142,'P052',700),
|
||||
('verse-0','And there has not arisen a',1320,2305,74,'Sanctification P052',500),
|
||||
('verse-1','prophet since in Israel like Moses,',1320,2413,74,'Sanctification P052',500),
|
||||
('verse-2','whom the LORD knew face to face',1320,2521,74,'Sanctification P052',500),
|
||||
('reference','Deuteronomy 34:10 • ESV',1320,2650,57,'Sanctification P052',500),
|
||||
]
|
||||
for ident,content,*_ in rows:
|
||||
fontfile=font_files['title' if ident=='title' else 'reference' if ident=='reference' else 'verse']
|
||||
charset=subprocess.check_output(['fc-query','--format=%{charset}',str(FONTS/fontfile)],text=True).split()
|
||||
ranges=[tuple(int(v,16) for v in token.split('-')) for token in charset]
|
||||
assert all(any(r[0]<=ord(char)<=r[-1] for r in ranges) for char in content), (ident, 'missing glyph')
|
||||
|
||||
texts=''.join(f'<text id="{ident}" x="{x}" y="{y}" font-family="{family}" font-weight="{weight}" font-size="{size}" text-anchor="middle" fill="#263f50">{escape(text)}</text>' for ident,text,x,y,size,family,weight in rows)
|
||||
(P/'text-preview.svg').write_text(head+texts+'</svg>');render(P/'text-preview.svg',P/'text-preview.png')
|
||||
text_image=Image.open(P/'text-preview.png').convert('RGBA');ta=np.asarray(text_image.getchannel('A'))
|
||||
assert not np.any((ta>0)&(fa>0))
|
||||
|
||||
(P/'border-backing-preview.svg').write_text(head+defs+frame+panels+'</svg>');render(P/'border-backing-preview.svg',P/'border-backing-preview.png')
|
||||
backing=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
|
||||
checks=[];verse_bounds=[]
|
||||
for ident,text,x,y,size,family,weight in rows:
|
||||
node=next(v for v in texts.split('</text>') if f'id="{ident}"' in v)+'</text>'
|
||||
src=P/(ident+'-ink.svg');dst=P/(ident+'-ink.png');src.write_text(head+node+'</svg>');render(src,dst)
|
||||
alpha=np.asarray(Image.open(dst).convert('RGBA').getchannel('A'));ys,xs=np.where(alpha>0);assert len(xs)
|
||||
bounds=[int(xs.min()),int(ys.min()),int(xs.max()+1),int(ys.max()+1)]
|
||||
safe=[1050,90,1905,330] if ident=='title' else [730,2180,1900,2690]
|
||||
assert safe[0]<=bounds[0] and safe[1]<=bounds[1] and bounds[2]<=safe[2] and bounds[3]<=safe[3],(ident,bounds,safe)
|
||||
assert np.all(backing[alpha>0]==255),ident
|
||||
assert not np.any((alpha>0)&(fa>0)),ident
|
||||
checks.append({'id':ident,'text':text,'baseline':y,'font':family,'weight':weight,'size':size,'inkBounds':bounds,'safeBounds':safe,'glyphPixelsOutsideOpaqueBacking':0,'foregroundIntersectionPixels':0})
|
||||
if ident.startswith('verse-'):verse_bounds.append(bounds)
|
||||
|
||||
assert ' '.join(c['text'] for c in checks if c['id'].startswith('verse-'))==json.loads((REV/'card.json').read_text())['excerpt']
|
||||
assert checks[-1]['text']==json.loads((REV/'card.json').read_text())['referenceDisplay']
|
||||
union=[min(b[0] for b in verse_bounds),min(b[1] for b in verse_bounds),max(b[2] for b in verse_bounds),max(b[3] for b in verse_bounds)]
|
||||
upper=2174;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3]
|
||||
assert min(above,below)>=35 and abs(above-below)<=20,(above,below)
|
||||
|
||||
for name,include_curtain in [('composed-preview',True),('without-curtain',False)]:
|
||||
body=base+frame+panels+(curtain if include_curtain else '')+texts
|
||||
src=P/(name+'.svg');src.write_text(head+defs+body+'</svg>');render(src,P/(name+'-2000.png'))
|
||||
master=Image.open(P/(name+'-2000.png')).convert('RGB')
|
||||
master.resize((1000,1400),Image.Resampling.LANCZOS).save(P/(name+'-1000.png'))
|
||||
master.resize((500,700),Image.Resampling.LANCZOS).save(P/(name+'-500.png'))
|
||||
|
||||
# Compare the structural effect of the attached curtain.
|
||||
board=Image.new('RGB',(1000,750),'#11161c');draw=ImageDraw.Draw(board)
|
||||
for i,(label,path) in enumerate([('Panels before foreground',P/'without-curtain-1000.png'),('Pillar of cloud over panels',P/'composed-preview-1000.png')]):
|
||||
draw.text((i*500+12,12),label,fill='#f4ead4')
|
||||
image=Image.open(path).convert('RGB');image.thumbnail((480,672));board.paste(image,(i*500+10,45))
|
||||
board.save(P/'curtain-comparison.png')
|
||||
|
||||
# A focused junction comparison at master crop.
|
||||
detail=Image.new('RGB',(1200,620),'#11161c');draw=ImageDraw.Draw(detail)
|
||||
for i,(label,path) in enumerate([('Without foreground',P/'without-curtain-2000.png'),('Cloud and curtain over backing',P/'composed-preview-2000.png')]):
|
||||
draw.text((i*600+12,12),label,fill='#f4ead4')
|
||||
image=Image.open(path).convert('RGB').crop((0,1780,900,2800));image.thumbnail((575,545));detail.paste(image,(i*600+12,52))
|
||||
detail.save(P/'curtain-junction-detail.png')
|
||||
|
||||
frame_alpha=np.asarray(Image.open(P/'border-backing-preview.png').convert('RGBA').getchannel('A'))
|
||||
foreground_frame_overlap=int(np.count_nonzero((fa>0)&(frame_alpha>0)))
|
||||
report={
|
||||
'status':'passed',
|
||||
'scope':'Provisional Legendary Normal — threshold of the Presence',
|
||||
'canvas':[2000,2800],
|
||||
'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash},
|
||||
'template':{
|
||||
'governingIdea':'The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached',
|
||||
'titleBackingPath':title_path,
|
||||
'verseBackingPath':verse_path,
|
||||
'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'],
|
||||
'curtainContour':'foreground-curtain.svg',
|
||||
'cloudUpperContour':cloud_top,
|
||||
'cloudLowerContour':cloud_low,
|
||||
'curtainAlphaBounds':list(Image.fromarray(fa).getbbox()),
|
||||
'curtainFrameOrBackingOverlapPixels':foreground_frame_overlap,
|
||||
},
|
||||
'typography':{
|
||||
'fontManifestSHA256':sha(FONTS/'manifest.json'),
|
||||
'checks':checks,
|
||||
'verseInkUnion':union,
|
||||
'upperFlourishToVerseInk':above,
|
||||
'verseInkToReferenceLineBox':below,
|
||||
'centeringDifference':abs(above-below),
|
||||
'lineBaselineGap':108,
|
||||
},
|
||||
'structuralChecks':{
|
||||
'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))),
|
||||
'glyphPixelsOutsideOpaqueBacking':0,
|
||||
'artworkSHA256Unchanged':True,
|
||||
'fontGlyphCoverage':'passed',
|
||||
'fontManifestHashes':'passed',
|
||||
},
|
||||
'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'],
|
||||
'staticReview':'Inspected at card size and enlarged cloud joins; provisional, pending user review',
|
||||
'edgeRendering':{'geometry':'source-traced cubic Bezier paths','leadWidthMasterPixels':8,'renderScale':2,'downsample':'premultiplied RGBA LANCZOS','blur':False},
|
||||
'foregroundAdaptation':'Source-registered cloud with retraced cubic perimeter and 8px dark lead; lower lobe closes before rocky-ground interruption. Source artwork, panel geometry and text unchanged.'
|
||||
}
|
||||
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
|
||||
assert sha(ART)==art_hash
|
||||
print(json.dumps({'status':'passed','preview':str((P/'composed-preview-1000.png').relative_to(REPO)),'curtainOverlapPixels':foreground_frame_overlap,'spacing':[above,below]},indent=2))
|
||||
|
||||
# Reproducible v3/v4 presentation and focused interaction review.
|
||||
label_font=ImageFont.truetype(str(FONTS/'P052-Bold.otf'),26)
|
||||
board=Image.new('RGB',(1500,1100),'#11161c');d=ImageDraw.Draw(board)
|
||||
for i,(label,path) in enumerate([('Astra v3 — initial contours',P.parent/'layout-preview-astra-v3/composed-preview-1000.png'),('Astra v4 — refined contours',P/'composed-preview-1000.png')]):
|
||||
d.text((i*750+24,18),label,font=label_font,fill='#f4ead4')
|
||||
im=Image.open(path).convert('RGB');im.thumbnail((730,1022));board.paste(im,(i*750+10,63))
|
||||
board.save(P/'v3-v4-comparison.png')
|
||||
master=Image.open(P/'composed-preview-2000.png').convert('RGB')
|
||||
detail=Image.new('RGB',(1500,1050),'#11161c');d=ImageDraw.Draw(detail)
|
||||
for label,box,origin,size in [('Cloud crowns the name',(570,0,1140,470),(22,65),(650,536)),('Pillar enters the verse field',(220,1960,850,2580),(795,65),(650,640))]:
|
||||
d.text((origin[0],20),label,font=label_font,fill='#f4ead4')
|
||||
im=master.crop(box);im.thumbnail(size);detail.paste(im,origin)
|
||||
im=master.crop((250,2080,1980,2780));im.thumbnail((1450,320));detail.paste(im,(25,722))
|
||||
detail.save(P/'cloud-interaction-detail.png')
|
||||
|
||||
# Verify the refinement has no layout/type drift from v3.
|
||||
previous=P.parent/'layout-preview-astra-v3'
|
||||
for filename in ['text-preview.png','border-backing-preview.png']:
|
||||
assert np.array_equal(np.asarray(Image.open(P/filename)),np.asarray(Image.open(previous/filename))), filename
|
||||
report['structuralChecks']['textPixelsIdenticalToV3']=True
|
||||
report['structuralChecks']['frameAndBackingPixelsIdenticalToV3']=True
|
||||
report['edgeRendering']['alphaEdge']='Premultiplied before downsampling; no transparent-RGB halo'
|
||||
(P/'preview-validation.json').write_text(json.dumps(report,indent=2)+'\n')
|
||||
# Matched enlarged crops make contour changes directly reviewable.
|
||||
board=Image.new('RGB',(1480,1430),'#11161c');d=ImageDraw.Draw(board)
|
||||
for col,(label,folder) in enumerate([('V3 — initial contour',previous),('V4 — refined lead contour',P)]):
|
||||
d.text((col*740+22,18),label,font=label_font,fill='#f4ead4')
|
||||
im=Image.open(folder/'composed-preview-2000.png').convert('RGB')
|
||||
for box,y,size in [((680,0,1030,400),65,(690,600)),((320,2070,765,2520),725,(690,670))]:
|
||||
crop=im.crop(box);crop.thumbnail(size);crop=crop.resize((round(crop.width*1.45),round(crop.height*1.45)),Image.Resampling.LANCZOS);crop.thumbnail(size);board.paste(crop,(col*740+22,y))
|
||||
board.save(P/'v3-v4-junction-comparison.png')
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
<fontconfig><dir>/home/dkzver/docker/sanctification/fonts</dir><cachedir>/home/dkzver/docker/sanctification/in-progress/cards/BP-001-moses/revisions/v03/review/layout-preview-astra-v4/font-cache</cachedir></fontconfig>
|
||||
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"status": "passed",
|
||||
"scope": "Provisional Legendary Normal \u2014 threshold of the Presence",
|
||||
"canvas": [
|
||||
2000,
|
||||
2800
|
||||
],
|
||||
"sourceArtwork": {
|
||||
"path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
|
||||
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
|
||||
"unchanged": true
|
||||
},
|
||||
"template": {
|
||||
"governingIdea": "The pillar of cloud enters the text field and crowns the name; the tent curtain remains attached",
|
||||
"titleBackingPath": "M700 62 H1900 Q1940 62 1940 102 V342 H700Z",
|
||||
"verseBackingPath": "M260 2092 H1895 Q1942 2092 1942 2139 V2712 Q1942 2758 1895 2758 H260Z",
|
||||
"compositionOrder": [
|
||||
"base-art",
|
||||
"frame-and-backings",
|
||||
"attached-curtain-foreground",
|
||||
"deterministic-text"
|
||||
],
|
||||
"curtainContour": "foreground-curtain.svg",
|
||||
"cloudUpperContour": "M714 0 C741 25 750 55 746 82 C787 58 814 43 846 45 C926 50 980 112 952 191 C1008 238 1002 319 925 369 L730 475 H0 V0Z",
|
||||
"cloudLowerContour": "M0 400H352 V1780H765 C765 1920 748 2010 736 2080 C728 2148 722 2213 710 2262 C704 2290 684 2310 660 2323 C650 2348 629 2367 608 2374 C599 2432 541 2477 480 2479 C435 2481 375 2418 354 2350 L350 2633 Q286 2659 205 2635 Q101 2583 0 2612Z",
|
||||
"curtainAlphaBounds": [
|
||||
0,
|
||||
0,
|
||||
996,
|
||||
2649
|
||||
],
|
||||
"curtainFrameOrBackingOverlapPixels": 342547
|
||||
},
|
||||
"typography": {
|
||||
"fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178",
|
||||
"checks": [
|
||||
{
|
||||
"id": "title",
|
||||
"text": "MOSES",
|
||||
"baseline": 244,
|
||||
"font": "P052",
|
||||
"weight": 700,
|
||||
"size": 142,
|
||||
"inkBounds": [
|
||||
1202,
|
||||
145,
|
||||
1711,
|
||||
247
|
||||
],
|
||||
"safeBounds": [
|
||||
1050,
|
||||
90,
|
||||
1905,
|
||||
330
|
||||
],
|
||||
"glyphPixelsOutsideOpaqueBacking": 0,
|
||||
"foregroundIntersectionPixels": 0
|
||||
},
|
||||
{
|
||||
"id": "verse-0",
|
||||
"text": "And there has not arisen a",
|
||||
"baseline": 2305,
|
||||
"font": "Sanctification P052",
|
||||
"weight": 500,
|
||||
"size": 74,
|
||||
"inkBounds": [
|
||||
890,
|
||||
2251,
|
||||
1749,
|
||||
2307
|
||||
],
|
||||
"safeBounds": [
|
||||
730,
|
||||
2180,
|
||||
1900,
|
||||
2690
|
||||
],
|
||||
"glyphPixelsOutsideOpaqueBacking": 0,
|
||||
"foregroundIntersectionPixels": 0
|
||||
},
|
||||
{
|
||||
"id": "verse-1",
|
||||
"text": "prophet since in Israel like Moses,",
|
||||
"baseline": 2413,
|
||||
"font": "Sanctification P052",
|
||||
"weight": 500,
|
||||
"size": 74,
|
||||
"inkBounds": [
|
||||
769,
|
||||
2359,
|
||||
1869,
|
||||
2435
|
||||
],
|
||||
"safeBounds": [
|
||||
730,
|
||||
2180,
|
||||
1900,
|
||||
2690
|
||||
],
|
||||
"glyphPixelsOutsideOpaqueBacking": 0,
|
||||
"foregroundIntersectionPixels": 0
|
||||
},
|
||||
{
|
||||
"id": "verse-2",
|
||||
"text": "whom the LORD knew face to face",
|
||||
"baseline": 2521,
|
||||
"font": "Sanctification P052",
|
||||
"weight": 500,
|
||||
"size": 74,
|
||||
"inkBounds": [
|
||||
752,
|
||||
2466,
|
||||
1886,
|
||||
2523
|
||||
],
|
||||
"safeBounds": [
|
||||
730,
|
||||
2180,
|
||||
1900,
|
||||
2690
|
||||
],
|
||||
"glyphPixelsOutsideOpaqueBacking": 0,
|
||||
"foregroundIntersectionPixels": 0
|
||||
},
|
||||
{
|
||||
"id": "reference",
|
||||
"text": "Deuteronomy 34:10 \u2022 ESV",
|
||||
"baseline": 2650,
|
||||
"font": "Sanctification P052",
|
||||
"weight": 500,
|
||||
"size": 57,
|
||||
"inkBounds": [
|
||||
991,
|
||||
2609,
|
||||
1649,
|
||||
2667
|
||||
],
|
||||
"safeBounds": [
|
||||
730,
|
||||
2180,
|
||||
1900,
|
||||
2690
|
||||
],
|
||||
"glyphPixelsOutsideOpaqueBacking": 0,
|
||||
"foregroundIntersectionPixels": 0
|
||||
}
|
||||
],
|
||||
"verseInkUnion": [
|
||||
752,
|
||||
2251,
|
||||
1886,
|
||||
2523
|
||||
],
|
||||
"upperFlourishToVerseInk": 77,
|
||||
"verseInkToReferenceLineBox": 86,
|
||||
"centeringDifference": 9,
|
||||
"lineBaselineGap": 108
|
||||
},
|
||||
"structuralChecks": {
|
||||
"curtainTextIntersectionPixels": 0,
|
||||
"glyphPixelsOutsideOpaqueBacking": 0,
|
||||
"artworkSHA256Unchanged": true,
|
||||
"fontGlyphCoverage": "passed",
|
||||
"fontManifestHashes": "passed",
|
||||
"textPixelsIdenticalToV3": true,
|
||||
"frameAndBackingPixelsIdenticalToV3": true
|
||||
},
|
||||
"notPerformed": [
|
||||
"Borderless/Textless/Boundless composition",
|
||||
"Production finish masks",
|
||||
"Production text masks",
|
||||
"Harness fixture installation",
|
||||
"GPU moving-light validation",
|
||||
"Final card approval"
|
||||
],
|
||||
"staticReview": "Inspected at card size and enlarged cloud joins; provisional, pending user review",
|
||||
"edgeRendering": {
|
||||
"geometry": "source-traced cubic Bezier paths",
|
||||
"leadWidthMasterPixels": 8,
|
||||
"renderScale": 2,
|
||||
"downsample": "premultiplied RGBA LANCZOS",
|
||||
"blur": false,
|
||||
"alphaEdge": "Premultiplied before downsampling; no transparent-RGB halo"
|
||||
},
|
||||
"foregroundAdaptation": "Source-registered cloud with retraced cubic perimeter and 8px dark lead; lower lobe closes before rocky-ground interruption. Source artwork, panel geometry and text unchanged."
|
||||
}
|
||||
Reference in New Issue
Block a user