import json,numpy as np,cv2,tifffile
from PIL import Image,ImageCms,ImageDraw
from pathlib import Path
src=json.load(open('/workspace/scratch/sources.json'));models=json.load(open('/workspace/scratch/finalcolour.json'))
logical={s:s for s in src};logical['HH-5547']='HH-6672';logical['HH-6672']='HH-5547'
out=Path('/workspace/output');(out/'assets').mkdir(exist_ok=True)
icc=open('/workspace/input/profiles/sRGB_icc22.icm','rb').read();handoff={'version':'ultimate-v13','skus':[]};meta={}
for sku,frame in logical.items():
 a=tifffile.imread('/workspace/input/'+src[frame]).astype('float32')/65535
 m=models[frame];z=np.where(a<=.04045,a/12.92,((a+.055)/1.055)**2.4)
 z=(np.maximum(z-np.array(m['input_offset'],dtype='float32'),1e-10)**np.array(m['gamma'],dtype='float32'))@np.array(m['matrix'],dtype='float32')*m['anchor']
 z=np.where(z<=.0031308,z*12.92,1.055*np.maximum(z,0)**(1/2.4)-.055)
 rgb=np.rint(np.clip(z,0,1)*255).astype('uint8');alpha=cv2.imread('/workspace/scratch/finalmasks/'+frame+'.png',0)
 yy,xx=np.where(alpha>0);x0=max(0,int(xx.min())-3);y0=max(0,int(yy.min())-3);x1=min(2400,int(xx.max())+4);y1=min(1600,int(yy.max())+4)
 rgba=np.dstack([rgb,alpha])[y0:y1,x0:x1]
 im=Image.fromarray(rgba,'RGBA');asset='assets/'+sku+'.png';im.save(out/asset,dpi=(300,300),icc_profile=icc,compress_level=6)
 handoff['skus'].append({'sku':sku,'source_frame':src[frame],'asset':asset,'crop_origin_px':[x0,y0]})
 meta[sku]={'frame':frame,'crop':[x0,y0,x1,y1],'size_px':[x1-x0,y1-y0],'mm':[(x1-x0)*25.4/300,(y1-y0)*25.4/300]}
 print(sku,meta[sku]['mm'])
json.dump(handoff,open(out/'handoff.json','w'),indent=2)
json.dump(meta,open('/workspace/scratch/assetmeta.json','w'),indent=2)
import json,numpy as np,cv2
from PIL import ImageFont,Image
from scipy import ndimage as ndi
plan=json.load(open('/workspace/input/page_plan.json'));meta=json.load(open('/workspace/scratch/assetmeta.json'));copy={p['sku']:p for p in json.load(open('/workspace/input/catalogue_copy.json'))['skus']}
copy['HH-3360']['price']='£94';copy['HH-3315']['price']='£185';copy['HH-7186']['name']='Rosewell bowl chandelier'
copy['HH-8256']['description']=copy['HH-8256']['description'].replace('The chimney lifts off for cleaning.','The chimney is fixed to the burner; clean it in place with a soft brush.')
copy['HH-9395']['description']=copy['HH-9395']['description'].replace('Finish with a clear oil to keep the grain bright.','It arrives with a clear hard-wax oil finish, so it needs no further treatment.')
reg=ImageFont.truetype('/workspace/input/NotoSans-Regular.ttf',1000);bold=ImageFont.truetype('/workspace/input/NotoSans-Bold.ttf',1000)
mmpt=25.4/72;pxmm=300/25.4;fs=10.0;leading=13.6*mmpt
width=lambda txt,font,size:font.getlength(txt)/1000*size*mmpt
layout=[]
for page in plan['pages']:
 p={'number':page['page'],'background':page['background'],'color':'White' if page['page'] in [1,3,5,7,8,9,11] else 'Black','products':[]}
 for r,sku in enumerate(page['skus']):
  c=copy[sku];w,h=meta[sku]['mm'];row=30+r*128;right=(r==0)
  imx=(198-w if w>=70 else 183-w) if right else (12 if w>=70 else 27)
  imy=row+21
  a=np.array(Image.open('/workspace/output/assets/'+sku+'.png'))[:,:,3]
  # Work on the external silhouette only. Padding is represented explicitly.
  full=ndi.binary_fill_holes(a>8)
  pad=45;full=np.pad(full,pad)
  dil=ndi.distance_transform_edt(~full)<=3.25*pxmm
  words=c['description'].split();idx=0;lines=[]
  start=imy+(h*.13 if w>=70 else h*.20)
  if sku=='HH-9374':start=imy+2
  for n in range(60):
   if idx>=len(words):break
   yy=start+n*leading
   y0=int((yy-imy)*pxmm)+pad;y1=int((yy+3.2-imy)*pxmm)+pad
   block=dil[max(0,y0):min(dil.shape[0],y1)]
   coords=np.where(block)[1]
   if len(coords):edge=(coords.min()-pad if right else coords.max()-pad)/pxmm+imx
   else:edge=(imx-3.25 if right else imx+w+3.25)
   if right:
    x1=edge; x0=max(12,x1-100)
   else:x0=edge;x1=min(198,x0+100)
   avail=x1-x0-.6
   if avail<22:
    print('NARROW',sku,n,avail)
   end=idx+1
   while end<=len(words) and width(' '.join(words[idx:end]),reg,fs)<=avail:end+=1
   end=max(idx+1,end-1);text=' '.join(words[idx:end]);idx=end
   lines.append({'text':text,'x':x0,'y':yy,'w':x1-x0,'h':5.1,'align':2 if right else 0,'font':'Noto Sans Regular','size':fs})
  prod={'sku':sku,'name':c['name'],'price':c['price'],'description':c['description'],'row':row,'image':[imx,imy,w,h],'right':right,'lines':lines}
  p['products'].append(prod)
  print(sku,'desc lines',len(lines),'end',round(lines[-1]['y']+4,1),'image',(round(imx,1),round(imy,1),round(w,1),round(h,1)))
 layout.append(p)
json.dump(layout,open('/workspace/scratch/layout.json','w'),indent=2)
json.dump(copy,open('/workspace/scratch/approved.json','w'),indent=2)
import scribus,os,json,traceback
try:
 L=json.load(open('/workspace/scratch/layout.json'))
 scribus.newDocument((210,297),(8,8,8,8),0,1,scribus.UNIT_MILLIMETERS,0,0,12)
 scribus.setInfo('Harbour & Hearth','Autumn / Winter catalogue','Twelve seasonal colour stories, twenty-four products')
 # Direct float process definitions; no spots or RGB brand substitutes.
 for p in L:scribus.defineColorCMYKFloat(p['background']['name'],*p['background']['cmyk'])
 over=[]
 def text(x,y,w,h,txt,font,size,color,name,align=0):
  o=scribus.createText(x,y,w,h,name);scribus.setText(txt,o);scribus.setFont(font,o);scribus.setFontSize(size,o);scribus.setTextColor(color,o);scribus.setTextDistances(0,0,0,0,o);scribus.setFirstLineOffset(scribus.FLOP_REALGLYPHHEIGHT,o);scribus.setLineSpacing(size*1.36,o);scribus.setTextAlignment(align,o);scribus.setFillColor('None',o);scribus.setLineColor('None',o)
  if scribus.textOverflows(o):over.append([name,txt])
  return o
 for p in L:
  pg=p['number'];scribus.gotoPage(pg);col=p['color'];b=p['background']['name']
  bg=scribus.createRect(-3,-3,216,303,'Page_%02d_Bleed'%pg);scribus.setFillColor(b,bg);scribus.setLineColor('None',bg)
  text(12,11,100,5,'HARBOUR & HEARTH','Noto Sans Bold',9,col,'Brand_%02d'%pg)
  text(128,11,70,5,'AUTUMN / WINTER','Noto Sans Regular',8.5,col,'Season_%02d'%pg,2)
  text(12,284,140,5,b.upper()+'  /  THE SEASONAL COLLECTION','Noto Sans Regular',8,col,'Footer_%02d'%pg)
  text(183,283.6,15,5,'%02d'%pg,'Noto Sans Bold',9,col,'Folio_%02d'%pg,2)
  for pr in p['products']:
   s=pr['sku'];row=pr['row'];x,y,w,h=pr['image'];im=scribus.createImage(x,y,w,h,'Product_'+s);scribus.loadImage('/workspace/output/assets/'+s+'.png',im);scribus.setImageScale(1,1,im);scribus.setImageOffset(0,0,im);scribus.setFillColor('None',im);scribus.setLineColor('None',im)
   text(12,row,158,7.2,pr['name'],'Noto Sans Bold',15.5,col,'Name_'+s)
   text(172,row+.4,26,7.2,pr['price'],'Noto Sans Bold',13,col,'Price_'+s,2)
   text(12,row+8,130,5,s,'Noto Sans Regular',9,col,'Code_'+s)
   for n,line in enumerate(pr['lines']):
    text(line['x'],line['y'],line['w'],line['h'],line['text'],line['font'],line['size'],col,'Description_'+s+'_%02d'%n,line['align'])
 scribus.saveDocAs('/workspace/output/catalogue.sla')
 open('/workspace/scratch/build_overflows.json','w').write(json.dumps(over,indent=2))
except Exception:open('/workspace/scratch/build_error.txt','w').write(traceback.format_exc())
os._exit(0)
from lxml import etree as E
import json
p='/workspace/output/catalogue.sla';t=E.parse(p);d=t.find('DOCUMENT')
d.set('HCMS','1');d.set('DIIm','1');d.set('DISc','1');d.set('DPbla','1');d.set('DPPr','ISO Coated v2 300% (basICColor)');d.set('DPIn','sRGB display profile (ICC v2.2)');d.set('DPIn2','sRGB display profile (ICC v2.2)');d.set('currentProfile','PDF/X-4');d.set('DFONT','Noto Sans Regular');d.set('DSIZE','10');d.set('TITLE','Harbour & Hearth — Autumn / Winter');d.set('AUTHOR','Harbour & Hearth');d.set('SUBJECT','Autumn / winter catalogue');d.set('KEYWORDS','Harbour & Hearth, autumn, winter, catalogue');d.set('AutoSave','0')
for k in ['BleedLeft','BleedRight','BleedTop','BleedBottom']:d.set(k,str(3*72/25.4))
colors={p['background']['name']:p['background']['cmyk'] for p in json.load(open('/workspace/input/page_plan.json'))['pages']}
for c in d.findall('COLOR'):
 if c.get('NAME') in colors:
  for k,v in zip(['C','M','Y','K'],colors[c.get('NAME')]):c.set(k,str(v))
for el in d.findall('PAGEOBJECT'):
 if el.get('PTYPE')=='2':
  el.set('IRENDER','1');el.set('LAYER','1');el.set('PFILE','assets/'+el.get('PFILE').split('/')[-1])
 elif el.get('PTYPE')=='4':el.set('LAYER','2')
 else:el.set('LAYER','0')
for l in d.findall('LAYERS'):d.remove(l)
for num,name,edit in [(0,'Full-bleed process colour',0),(1,'Cut-out masters · 300 ppi',1),(2,'Live catalogue typography',1)]:
 d.insert(list(d).index(d.find('Printer')),E.Element('LAYERS',NUMMER=str(num),LEVEL=str(num),NAME=name,SICHTBAR='1',DRUCKEN='1',EDIT=str(edit),SELECT='0',FLOW='1',TRANS='1',BLEND='0',OUTL='0',LAYERC='#668899'))
d.set('ALAYER','2')
pdf=d.find('PDF')
for k,v in {'Version':'10','RGBMode':'0','UseProfiles':'0','UseProfiles2':'1','Intent':'1','Intent2':'1','PrintP':'ISO Coated v2 300% (basICColor)','InfoString':'Harbour & Hearth — Autumn / Winter','useDocBleeds':'1','BTop':str(3*72/25.4),'BLeft':str(3*72/25.4),'BRight':str(3*72/25.4),'BBottom':str(3*72/25.4),'Compress':'1','CMethod':'2','Quality':'0','RecalcPic':'0','PicRes':'300','Resolution':'300','FontEmbedding':'0','UseSpotColors':'0','firstUse':'0'}.items():pdf.set(k,v)
t.write(p,xml_declaration=True,encoding='UTF-8',pretty_print=True)
import scribus,os,traceback,json
try:
 scribus.openDoc('/workspace/output/catalogue.sla')
 p=scribus.PDFfile();p.file='/workspace/output/catalogue_press.pdf';p.pages=list(range(1,13));p.version=10;p.info='Harbour & Hearth — Autumn / Winter';p.outdst=1;p.printprofc='ISO Coated v2 300% (basICColor)';p.intenti=1;p.intents=1;p.profilei=1;p.profiles=0;p.noembicc=0;p.usespot=0;p.compressmtd=2;p.quality=0;p.downsample=0;p.resolution=300;p.useDocBleeds=1;p.fontEmbedding=0;p.fonts=['Noto Sans Regular','Noto Sans Bold'];p.subsetList=[];p.save()
 scribus.saveDocAs('/workspace/output/catalogue.sla')
except Exception:open('/workspace/scratch/export_error.txt','w').write(traceback.format_exc())
os._exit(0)
