Add Legendary Moses card and production guidance

This commit is contained in:
2026-09-17 09:26:45 -07:00
parent ee0113442d
commit 5e49435127
514 changed files with 9974 additions and 13 deletions

View File

@@ -0,0 +1,36 @@
# Moses — provisional Legendary compact textile composition
This preview revises the selected v03 illustration with a narrower, tent-derived backing and a three-line verse setting. It is a composition candidate, not production assembly.
- [Composed preview](composed-preview-1000.png)
- [500 × 700 preview](composed-preview-500.png)
- [v1 / v2 backing comparison](backing-comparison.png)
- [Curtain comparison](curtain-comparison.png)
- [Enlarged curtain/backing junction](curtain-junction-detail.png)
- [Authored curtain foreground](foreground-curtain.svg)
- [Frame and backing source](border-backing-preview.svg)
- [Deterministic text source](text-preview.svg)
- [Validation](preview-validation.json)
- [Reproducible builder](build-preview.py)
## Governing idea
The outer frame uses narrow antique gold, lapis, ivory and tent-derived geometric bands. The upper-right plaque now reads as a sloped canopy rather than a chamfered royal plaque. The lower ivory backing enters beneath the curtain, then steps inward to form a compact woven screen. Burgundy, lapis, and restrained gold bands replace the centered lozenge flourish language used on David.
The attached burgundy curtain remains above the frame and backing. The backing no longer spans the unused lower-left bay: its visible edge follows the threshold transition, while the three-line verse expands across the usable textile field. Moses stays behind the backing.
The narrative verse is presented without quotation marks:
And there has not arisen a prophet since<br>
in Israel like Moses, whom the LORD<br>
knew face to face
Deuteronomy 34:10 • ESV
P052 Bold 700 renders MOSES at 138px. Sanctification P052 Medium 500 renders the verse at 72px with 104px baselines and the reference at 57px. Validation measures the actual verse ink between the upper textile band and the top of the reference.
## Scope
Only the provisional Normal composition is rendered. The proposed printing behavior remains a hypothesis until this layout is accepted: Normal would use frame, backings, curtain, and text; Borderless would use backings, curtain, and text; Textless would use frame and curtain; Boundless would use the unmodified illustration.
No production printings, finish masks, text masks, harness fixtures, moving-light review, or final approval were created.

View File

@@ -0,0 +1,149 @@
#!/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):ink(src,'--export-type=png',f'--export-filename={dst}','--export-width=2000','--export-height=2800')
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>'''
title_path='M1080 72 H1888 L1940 124 V342 H1045 V148 Z'
verse_path='M445 2088 H1896 Q1942 2088 1942 2134 V2719 Q1942 2765 1896 2765 H495 Q460 2765 460 2730 V2260 L405 2190 V2128 Q405 2088 445 2088 Z'
title_inner='<path d="M1092 92 H1878 L1918 132 V320 H1067 V156 Z" fill="none" stroke="#9b732b" stroke-width="3"/><path d="M1110 299H1880" stroke="#7f2934" stroke-width="4"/><path d="M1110 309H1880" stroke="#173e59" stroke-width="5"/><path d="M1120 317H1870" stroke="#b98a32" stroke-width="2"/><g fill="#b98a32"><path d="M1190 297l9 9-9 9-9-9Z"/><path d="M1500 297l9 9-9 9-9-9Z"/><path d="M1810 297l9 9-9 9-9-9Z"/></g>'
verse_inner='<path d="M458 2108 H1882 Q1922 2108 1922 2148 V2705 Q1922 2745 1882 2745 H510 Q480 2745 480 2715 V2252 L425 2182 V2141 Q425 2108 458 2108Z" fill="none" stroke="#9b732b" stroke-width="3"/><path d="M650 2144H1840" stroke="#7f2934" stroke-width="4"/><path d="M650 2154H1840" stroke="#173e59" stroke-width="5"/><path d="M650 2162H1840" stroke="#b98a32" stroke-width="2"/><path d="M650 2691H1840" stroke="#7f2934" stroke-width="4"/><path d="M650 2701H1840" stroke="#173e59" stroke-width="5"/><path d="M650 2709H1840" stroke="#b98a32" stroke-width="2"/><g fill="#b98a32"><path d="M760 2142l10 10-10 10-10-10Z"/><path d="M1245 2142l10 10-10 10-10-10Z"/><path d="M1730 2142l10 10-10 10-10-10Z"/><path d="M760 2689l10 10-10 10-10-10Z"/><path d="M1245 2689l10 10-10 10-10-10Z"/><path d="M1730 2689l10 10-10 10-10-10Z"/></g>'
def panel(path,inner,ident):return f'<g id="{ident}"><path d="{path}" fill="url(#paper)" stroke="#5f431d" stroke-width="10"/><path d="{path}" fill="url(#weave)" stroke="url(#gold)" stroke-width="5"/>{inner}</g>'
panels=panel(title_path,title_inner,'title-backing')+panel(verse_path,verse_inner,'verse-backing')
curtain=f'<g id="curtain-foreground" clip-path="url(#curtain-clip)"><image xlink:href="../../source/art-master.png" width="2000" height="2800"/></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',1500,244,138,'P052',700),
('verse-0','And there has not arisen a prophet since',1190,2308,72,'Sanctification P052',500),
('verse-1','in Israel like Moses, whom the LORD',1190,2412,72,'Sanctification P052',500),
('verse-2','knew face to face',1190,2516,72,'Sanctification P052',500),
('reference','Deuteronomy 34:10 • ESV',1190,2650,57,'Sanctification P052',500),
]
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=[1080,90,1905,330] if ident=='title' else [500,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=2165;ref_top=checks[-1]['inkBounds'][1];above=union[1]-upper;below=ref_top-union[3]
assert min(above,below)>=35 and abs(above-below)<=4,(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([('Backing and frame alone',P/'without-curtain-1000.png'),('Threshold curtain overlap',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'),('Curtain over frame/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 compact textile composition only',
'canvas':[2000,2800],
'sourceArtwork':{'path':str(ART.relative_to(REPO)),'sha256':art_hash,'unchanged':sha(ART)==art_hash},
'template':{
'governingIdea':'Tent threshold with compact woven screen',
'titleBackingPath':title_path,
'verseBackingPath':verse_path,
'compositionOrder':['base-art','frame-and-backings','attached-curtain-foreground','deterministic-text'],
'curtainContour':'foreground-curtain.svg',
'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':104,
},
'structuralChecks':{
'curtainTextIntersectionPixels':int(np.count_nonzero((ta>0)&(fa>0))),
'glyphPixelsOutsideOpaqueBacking':0,
'artworkSHA256Unchanged':True,
},
'notPerformed':['Borderless/Textless/Boundless composition','Production finish masks','Production text masks','Harness fixture installation','GPU moving-light validation','Final card approval'],
'staticReview':'pending user review'
}
(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))

View File

@@ -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-v2/font-cache</cachedir></fontconfig>

View File

@@ -0,0 +1,171 @@
{
"status": "passed",
"scope": "Provisional Legendary Normal compact textile composition only",
"canvas": [
2000,
2800
],
"sourceArtwork": {
"path": "in-progress/cards/BP-001-moses/revisions/v03/source/art-master.png",
"sha256": "73f933cf34719dfda09b719376948bcf072bec504a88329b5a22e4ac1ed76d85",
"unchanged": true
},
"template": {
"governingIdea": "Tent threshold with compact woven screen",
"titleBackingPath": "M1080 72 H1888 L1940 124 V342 H1045 V148 Z",
"verseBackingPath": "M445 2088 H1896 Q1942 2088 1942 2134 V2719 Q1942 2765 1896 2765 H495 Q460 2765 460 2730 V2260 L405 2190 V2128 Q405 2088 445 2088 Z",
"compositionOrder": [
"base-art",
"frame-and-backings",
"attached-curtain-foreground",
"deterministic-text"
],
"curtainContour": "foreground-curtain.svg",
"curtainAlphaBounds": [
0,
0,
769,
2800
],
"curtainFrameOrBackingOverlapPixels": 181809
},
"typography": {
"fontManifestSHA256": "ef68ee6393981d54ca068a950f21c53b374d20e6e46c21c788f36380d3aae178",
"checks": [
{
"id": "title",
"text": "MOSES",
"baseline": 244,
"font": "P052",
"weight": 700,
"size": 138,
"inkBounds": [
1251,
148,
1746,
247
],
"safeBounds": [
1080,
90,
1905,
330
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-0",
"text": "And there has not arisen a prophet since",
"baseline": 2308,
"font": "Sanctification P052",
"weight": 500,
"size": 72,
"inkBounds": [
548,
2255,
1831,
2329
],
"safeBounds": [
500,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-1",
"text": "in Israel like Moses, whom the LORD",
"baseline": 2412,
"font": "Sanctification P052",
"weight": 500,
"size": 72,
"inkBounds": [
598,
2359,
1782,
2424
],
"safeBounds": [
500,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
},
{
"id": "verse-2",
"text": "knew face to face",
"baseline": 2516,
"font": "Sanctification P052",
"weight": 500,
"size": 72,
"inkBounds": [
919,
2463,
1460,
2518
],
"safeBounds": [
500,
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": [
861,
2609,
1519,
2667
],
"safeBounds": [
500,
2180,
1900,
2690
],
"glyphPixelsOutsideOpaqueBacking": 0,
"foregroundIntersectionPixels": 0
}
],
"verseInkUnion": [
548,
2255,
1831,
2518
],
"upperFlourishToVerseInk": 90,
"verseInkToReferenceLineBox": 91,
"centeringDifference": 1,
"lineBaselineGap": 104
},
"structuralChecks": {
"curtainTextIntersectionPixels": 0,
"glyphPixelsOutsideOpaqueBacking": 0,
"artworkSHA256Unchanged": true
},
"notPerformed": [
"Borderless/Textless/Boundless composition",
"Production finish masks",
"Production text masks",
"Harness fixture installation",
"GPU moving-light validation",
"Final card approval"
],
"staticReview": "pending user review"
}