Commits

Negre pur a fons, multi-edició a producció i els visors de PDF en un dia

Revisió profunda del K-only al cos del text, desplegament de multi-edició a magazine.escandell.cat i cinc dels sis tickets dels visors de PDF: iframe per al número, i fonts, colors, mapa de calor i planxes per a impremta.

· auto-magazine · milestone

El dimecres va començar amb una revisió a fons i va acabar amb la funcionalitat sencera a producció i una altra de nova dissenyada, construïda i revisada en un sol dia.

El negre pur revisat a fons

Tot va empezar perquè l'usuari va demanar revisió profunda del negre pur: no tots els textos sortien en K. L'inventari va trobar dos forats. El gros: l'estil mai aplicava la tinta al cos — color: var(--color-tinta) només existia al foli i als enllaços de l'índex, així que tot el text normal sortia del negre inicial de CSS, aquell rgb(0,0,0) que Ghostscript converteix en rich black de quatre planxes. L'adob de rel va ser una línia: body { color: var(--color-tinta) }. El segon forat eren quatre grisos literals (entradeta, peus de foto, entradeta de portada, el colofó) convertits en variables que, amb el checkbox activat, es defineixen com K-only amb el seu percentatge. Els colors de marca es queden en CMYK de quatre planxes, com cal. La verificació amb el PDF d'impremta real va mostrar 19 farcits en K pur i una cosa curiosa: els tres rich blacks restants viuen dins dels PDF del client — el seu art final, preservat tal com és, amb tinta total 295% dins del límit del preflight. I una lliçó incòmoda: l'anàlisi visual per imatge va al·lucinar una cita inexistent — les transcripcions d'imatge es verifiquen contra el disc, mai es donen per bones.

Multi-edició a producció

A les onze del matí, multi-edició va arribar a producció: merge fast-forward de feature/multi-edicion a main (25 commits), push i desplegament a fxserver amb docker compose up --build. Health 200 i arrencada neta del contenidor. L'arrencada neta va fer el previst amb el material de la prova del MVP: arxivat a la paperera i hotfolder a punt per a les edicions de veritat. El que veu l'usuari ara a magazine.escandell.cat és el llistat d'edicions buit esperant la primera edició real, la Identitat configurable amb colofó i negre pur, la fitxa amb portada i contraportada, i la Neteja amb el material arxivat.

Dos visors de PDF, dissenyats al grilling

Abans de la llista de poliment, l'usuari va encarregar la funcionalitat següent: dos visors de PDF. El Visor del número resol una fricció simple — «Veure PDF» avui descarrega l'arxiu — amb una pàgina d'iframe i el visor natiu del navegador, zero JS. El Visor d'impremta és una altra història: l'eina de validació abans d'enviar al taller, inspirada en les eines de tiny-online però podada fins a allò útil — inspector de fonts, colors per pàgina amb avís RGB, mapa de calor de tinta reusant el càlcul del preflight, caixes superposades en només lectura i la petició estrella de l'usuari: planxes, veure cada pàgina només amb la tinta C, M, Y o K que triïs, via tiffsep de Ghostscript. Dues rondes de grilling van deixar les decisions clares: tot server-side amb la toolchain del contenidor (pikepdf, Ghostscript, poppler, sense PDF.js), una sola pàgina amb panels, el visor absorbeix l'informe de preflight actual i les planxes es calculen a demanda amb caché invalidada al re-exportar.

Tardes de tickets: cinc de sis

La tarda va ser de tickets, i en un dia en van caure cinc dels sis. El Visor del número va funcionar a la primera llevat d'un detall de Starlette 1.10, que no té el kwarg nou de content-disposition. La troballa del ticket va ser el middleware: posava X-Frame-Options: DENY global, que hauria trencat l'iframe en sec — DENY prohibeix embeure fins i tot same-origin. Canviat a SAMEORIGIN i test de regressió perquè no torni a passar. L'esquelet del Visor d'impremta va arribar amb la seva caché a salida/nNN/.visor/ i un threading.Lock que serialitza la generació: la primera ràfaga de miniatures demana en paral·lel i sense lock es picaven el borrat i el move. La lliçó TDD del ticket: el paràmetre es deia tamano al router i tam a les URLs, FastAPI l'ignorava i servia sempre la imatge gran — els tests només comptaven URLs a l'HTML i no el cazaven; el test que demana una miniatura de veritat el va destapar.

Els panels d'anàlisi (checks, fonts i colors) van portar els descobriments més fins. L'anàlisi recorre el document una vegada amb parse_content_stream de pikepdf seguint també els XObjects Form en cadena, perquè les portades PDF de client porten forms d'Illustrator. WeasyPrint estableix el color de text fora de BT/ET, així que l'atribució text/vector és mandrosa: el color es compta on es gasta, als Tj/TJ o als paths. I el FontFile de les fonts CID viu al FontDescriptor del descendent, no al dict arrel. Els code reviews posteriors van caçar dos bichos seriosos reproduïts pel reviewer: un Form XObject cíclic penjava el lock global del visor sencer, i la detecció de fonts sense usar comparava noms de recurs globals que poden referir fonts diferents a cada pàgina. Tots dos s'arreglen igual: identitat per objecte, no per nom.

Mapes de calor i planxes

Les capes de caixes i mapa de calor van venir amb tres ensopegades de Ghostscript documentades com a shortcut: gs 10.08 rebenta amb -dFirstPage/-dLastPage en tiff32nc, així que qpdf extreu la pàgina i gs la rasteritza sola; sense -dNOPAUSE -dBATCH es queda interactiu si stdin és viu; i Image.point no suporta LUT de L a RGBA, quatre LUTs de canal i merge. El descobriment de domini va ser bo: gs converteix tot el negre RGB en rich black de ~295%, així que amb el límit per defecte de 300 el text normal no es veu vermell al mapa — el vermell senyala de veritat les zones carregades. El code review va caçar el gros del dia: la caché no era atòmica i un procés mort a mitja escriptura deixava un JSON truncat amb índex vàlid, un 500 permanent fins re-exportar. Ara tot s'escriu via temporal + os.replace, amb versió al nom de l'arxiu.

I al vespre, la petició estrella: planxes. Radios de Compaginat, C, M, Y i K a la barra, i les tintes directes es descobreixen soles preguntant a l'endpoint de llista — un radio per spot que gs hagi separat. tiffsep escriu un TIFF per planxa, els noms surten del propi arxiu i l'endpoint resol la planxa demanada contra els arxius reals de la caché, mai es construeix una ruta des de la URL. El bug del camí va ser petit i didàctic: retornar una HTTPException com a valor de retorn del helper no l'eleva — FastAPI no la converteix en 404, em va donar un 200 raro que va costar caçar. El code review final va deixar la suite a 116 tests en verd, amb el crític resolt: sense onerror, un 404 de planxa deixava el visor amb «generant…» per sempre.

Al tancament del dia, la funcionalitat va 5 de 6 tickets i tota la feina del dimecres suma una suite de 116 tests en verd. Queda el ticket 06, l'end-to-end amb material real, i el desplegament.