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)
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 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)
# Grading (ultimate-v13)

Each of the 24 SKUs is worth 1/24 of the score. Within a SKU (weights fixed before any trial of this version;
the cut-out is the paid deliverable of a silhouetting job, the other items are routine production checks):

- 75% silhouette: the product's effective coverage as actually printed in catalogue_press.pdf (the combined
  effect of any clipping path, soft mask or alpha, recovered by substituting two flat colours for the image
  data and comparing Ghostscript 10.02.1 RGB renders with printer options;
  printable annotations and declared Usage/Print layer states participate, and nonprinting annotations do not;
  declared ON/OFF print states are normalized consistently before all print-composition checks) is mapped to the master pixel grid through the image placement and the
  declared, verified crop origin, and compared with the true product coverage of the frame. Boundary F-measure at
  2 px (0.17 mm at 300 ppi): 0.95 or better earns full credit, 0.70 or worse none, linear between. For the
  soft-edge SKUs both coverages are first smoothed with a 3 px Gaussian, so the outline of the fibre mass is
  graded and individual fibres are not (same F-measure and thresholds). Silhouette credit additionally
  requires no visible local residual background or missing product beyond the physical 0.2 mm proof band (2.3622 px at 300 ppi). After the same
  soft-mass treatment, extra and missing coverage are checked separately: an 8-connected wrong-coverage
  region outside that proof band with at least six pixels (the area of a 0.2 mm square rounded up at 300 ppi)
  rejects that SKU's silhouette item. Continuous coverage is checked as well: at each pixel it must stay
  within the minimum and maximum true coverage in the same circular 0.2 mm neighbourhood, allowing
  3/255 coverage for eight-bit raster quantization. Connected excess or deficit beyond that envelope
  follows the same six-pixel rule. For soft-edge SKUs the candidate coverage and the permitted minimum/maximum envelope are smoothed by the same 3 px Gaussian, preserving the geometric freedom of individual fibres; partially transparent
  background residue and faded opaque product interiors cannot evade the check by staying on one side
  of the half-coverage threshold. Tiny isolated quantization pixels do not trigger this condition.
- 10% colour: the product region (true coverage, eroded 3 px) as printed is converted through ISO Coated v2 300%
  to CIELAB D50 and compared with the correctly profiled frame (the frame rendered with a correct camera, at the
  delivered brightness of neutral patch 22), reproduced through the same press condition. Two equal parts: CIEDE2000 between the region means (2 or
  less earns full credit, 6 or more none) and the 90th percentile of CIEDE2000 over 16 x 16 px block means (3 or
  less full, 8 or more none; local blocks carry the frames' sensor noise and shadow quantisation).
- 5% wrap: description lines beside the product (sharing its vertical band) are measured from their glyph boxes to
  the true silhouette at its printed position. A line qualifies when it keeps at least 2.5 mm from the silhouette and
  its facing edge is within 6 mm of it (the description's final line only needs the 2.5 mm clearance). Credit = qualifying lines / max(4, lines beside the product).
- 10% source and copy, five equal parts: correct source frame (the declared frame shows this SKU's product and
  follows the delivery-note corrections, and the printed product keeps the master's fine detail: at least 85% of its fine-detail pattern is retained: after a 0.6 px Gaussian, the demeaned Laplacian of the candidate's press lightness is projected onto the corrected delivered master's demeaned Laplacian inside the product; both the retained contrast and normalized fine-pattern correlation must be at least 0.85. The corrected master is produced from the public source and measured chart and includes the source sensor detail. Unrelated noise, denoising and sharpened upsampled proxies cannot replace the original spatial pattern); effective resolution
  at least 280 ppi; the finally approved name and price (copy deck plus client amendments) and the SKU code as
  visible live text on the SKU's page, with no superseded name or price on that page; the complete finally approved
  description (at least 98% of its words in order) with no superseded sentence; the placed asset linked from catalogue.sla (all pages) with the handoff version: a readable relative PNG/TIFF link, matching master pixels and placement, approved live copy that remains visibly complete on the native page after an actual reopening/export, and a Scribus reopening/export that reproduces the delivered product region within the same full-credit colour tolerances (mean ΔE00 ≤ 2 and block p90 ≤ 3), with its effective coverage agreeing
  with the delivered PDF at boundary F ≥ 0.95 and with no visible local extra/missing region under the same
  0.2 mm / six-connected-pixel condition, including the continuous-coverage envelope and its 3/255 quantization allowance. Empty spaces in the image crop are included in this native comparison.

A product whose placement cannot be identified earns no silhouette, colour or wrap credit. Missing or unreadable
deliverables lose the dependent items only. Broken evaluator inputs or software are infrastructure errors (no
score). The PDF/X-4 output intent, trim/bleed boxes and embedded fonts are checked as preconditions of the
colour item (PDF/X-4 declaration in XMP, the complete supplied ISO Coated v2 300% profile, all four bleed edges and actual embedded font programs; a missing precondition earns no colour credit). The ICC creation date and profile ID may differ without penalty. Colour is measured from the composed page through Ghostscript 10.02.1 with relative colorimetric intent and black-point compensation; overlays and transparency therefore count. A native validation export runs offline with only candidate files, public fonts/profiles and the specified Scribus software. Unreadable image objects are omitted from that temporary validation copy and cannot earn their own native credit; unrelated linked products remain assessable.

The unchanged page and pixel-grid requirements qualify the existing components. Both image axes must be
upright at 300 ± 3 ppi for silhouette, colour and wrap measurements; the resolution source sub-item requires
both axes to be at least 280 ppi. Silhouette credit also requires the product coverage to stay inside trim and
not overlap another product. The colour item requires the full-bleed brand fill outside product and glyph
areas to meet the same full-credit mean ΔE00 ≤ 2 and block p90 ≤ 3 tolerances against the prescribed process
CMYK colour. The flat fill also requires every measured 72-ppi background sample to have ΔE00 ≤ 3;
small foreign patches cannot be diluted in the page average. Boundary antialiasing is excluded at the
72-ppi background check. Product exclusion includes all true matte coverage above 1/255, so natural
low-opacity fur tips are excluded from flat-background samples.

Approved name/price/code credit requires visible glyphs, 8-mm trim clearance and no product overlap; names
use Noto Sans Bold 14–18 pt. Approved description credit and wrap credit require visible Noto Sans Regular
9–11 pt glyphs with the same page and product clearance. Font sizes allow 0.05 pt serialization rounding.
Visible copy is checked against the actual embedded glyph program as well as Unicode and font labels.
For Noto Sans, painted outlines must match the supplied font's corresponding Unicode glyph or legitimate
OpenType alternate; subset numbering and font names do not establish character identity. Other embedded
price/code fonts must retain their program's Unicode-to-glyph correspondence. Copy is also checked with
text rendering modes and opacity, plus a 300-ppi comparison of the actual printed composition against the same
page with only live text removed. Both rasters use the pinned Ghostscript press conversion with print
options and annotations enabled; printable and print-only annotations participate in occlusion. Nonprinting
annotations do not hide copy in the print visibility check. A retained non-space glyph must contribute at least one pixel with
8/255 RGB contrast attributable to that glyph. If live glyph cells overlap, changes assigned to a later
painted glyph cannot be borrowed by an earlier hidden glyph; antialiasing is included with a one-raster-pixel
cell margin. Invisible, transparent, occluded, clipped and white-on-white text cannot earn credit.
These checks qualify the original items; no weights or scoring budget are changed.

Copy and description wrap must stay outside the product’s outer outline: enclosed transparent gaps are excluded from copy placement and wrap-distance credit. This affects copy/wrap checks only; actual cut-out transparency, product colour, background and silhouette metrics retain the true coverage.
