feat: accept large JPG and WebP artworks instead of refusing them

Above 150 MB the browser cannot read JPG or WebP in parts, so a large
artwork was refused. It is now measured from its header, graded and priced
with the discount like any other, and placed on the sheet as a full box,
which is exact for JPG. The card says there is no preview and that the team
checks the art before printing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-30 12:14:04 -03:00
parent 933bd30cbd
commit b7e2ea12d0
6 changed files with 23 additions and 11 deletions

View File

@@ -91,7 +91,7 @@ function carrinho(){
measurement:x.med?'file':'customer'}
: {kind:'artwork',width_cm:x.cm,length_cm:+(x.cm*propDe(x)).toFixed(4),
copies:x.q,rotation_degrees:x.giro||0,mirrored:!!x.esp,
measurement:x.src?'file':'customer'});
measurement:(x.src||x.semPrevia)?'file':'customer'});
let layout;
if(ehFolha()){
let y=0;

View File

@@ -337,7 +337,7 @@ function previaAoVivo(){
tMont=setTimeout(()=>{
// o produto pode ter sido fechado nesses 140 ms (item foi para o carrinho)
if(!modo) return;
const prontas=artes.filter(a=>a.cm>0 && a.cm<=larguraFilme() && a.src);
const prontas=artes.filter(a=>a.cm>0 && a.cm<=larguraFilme() && (a.src||a.semPrevia));
const W=Math.round(300*ZOOMS[zoomI]);
$('vArea').classList.toggle('ampliado', zoomI>0);
desenhaMontagem($('vArea'), prontas, 0, W, $('vUso'), r=>{

View File

@@ -541,13 +541,11 @@ function pintaArtes(){
if(a.lendo!=null){
barra='<div class="prog"><i style="width:'+a.lendo+'%"></i></div>'+
'<span class="progT">arquivo grande · gerando a prévia… '+a.lendo+'%</span>';
}else if(a.decodeError==='grande'){
aviso='<p class="avisoF"><b>Arquivo grande em JPG ou WebP.</b> Exporte em PNG para montar a folha.</p>';
}else if(a.decodeError){
aviso='<p class="avisoF"><b>Não conseguimos ler esta imagem.</b> Exporte novamente em PNG ou JPG.</p>';
}else if(a.cm>larguraFilme()){
aviso='<p class="avisoF"><b>Largura maior que o filme.</b> O filme aqui tem '+n1(larguraFilme())+' cm.</p>';
}else if(a.cm>0 && a.src){
}else if(a.cm>0 && (a.src||a.semPrevia)){
const dpi=Math.round(dpiDe(a));
medida='<b>'+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm</b>';
chips='<ul class="chips"><li class="chip '+(dpi>=DPI_AVISA?'ok':dpi>=DPI_RECUSA?'av':'er')+'">'+dpi+' DPI</li>'+
@@ -564,8 +562,11 @@ function pintaArtes(){
n1(sug.cm)+' cm.<button data-usar="'+i+'" data-cm="'+sug.alvo+'">Usar '+n1(sug.alvo)+' cm</button></div>'
: '';
}
if(a.semPrevia && !a.decodeError)
aviso+='<p class="repTxt"><b>Sem prévia para '+escapeHTML(extDe(a.f.name))+' deste tamanho.</b> '+
'A arte entra na folha pela medida, e a equipe confere antes de imprimir. Em PNG, a prévia aparece.</p>';
const mini = a.miniSrc ? '<img src="'+escapeHTML(a.miniSrc)+'" alt="">' : '<span>'+escapeHTML(extDe(a.f.name))+'</span>';
const sub = a.cm>0 && a.src ? (a.q>1? a.q+' × ' : '')+'<b>'+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm</b>' : '';
const sub = a.cm>0 && (a.src||a.semPrevia) ? (a.q>1? a.q+' × ' : '')+'<b>'+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm</b>' : '';
return '<div class="rep art2'+(a===recemChegada?' nova':'')+'" data-arte="'+i+'">'+
'<div class="repTopo"><div class="miniF">'+mini+'</div><div class="repInfo">'+
'<div class="l1"><b>'+escapeHTML(a.f.name)+'</b>'+(a===recemChegada?'<em class="novaT">nova</em>':'')+
@@ -622,7 +623,7 @@ function pintaArtes(){
previaAoVivo();
}
function resumoArtes(){
const r=$('rA'), prontas=artes.filter(a=>a.cm>0 && a.cm<=larguraFilme() && a.src);
const r=$('rA'), prontas=artes.filter(a=>a.cm>0 && a.cm<=larguraFilme() && (a.src||a.semPrevia));
if(!prontas.length){ r.style.display='none'; return; }
const m=montagemCm/100; // a mesma folha que aparece ao lado
const notas=prontas.map(a=>Math.max(6,Math.min(100,Math.round(dpiDe(a)/300*100))));

View File

@@ -64,9 +64,7 @@ function avaliar(){
}
if(!artes.length) return {pronto:false, falta:'Arraste suas artes para começar.'};
if(artes.some(a=>a.lendo!=null)) return {pronto:false, falta:'Gerando a prévia das artes grandes…'};
if(artes.some(a=>a.decodeError==='grande'))
return {pronto:false, falta:'Uma arte grande está em JPG ou WebP. Exporte-a em PNG para montar a folha.'};
if(artes.some(a=>a.decodeError || !a.src || !(a.px>0) || !(a.py>0)))
if(artes.some(a=>a.decodeError || !(a.src||a.semPrevia) || !(a.px>0) || !(a.py>0)))
return {pronto:false, falta:'Não conseguimos ler uma das imagens. Exporte-a novamente em PNG ou JPG antes de continuar.'};
if(artes.some(a=>!Number.isFinite(a.cm) || a.cm>larguraFilme()))
return {pronto:false, falta:'A largura da arte precisa caber no filme de '+

View File

@@ -128,7 +128,9 @@ function medir(a){
return dimensoesImagem(a.f).then(async d=>{
if(!d || !(d.w>0) || !(d.h>0)){ a.decodeError=true; return a; }
a.px=d.w; a.py=d.h; a.prop=d.h/d.w; a.grande=true;
if(!/\.png$/i.test(a.f.name)){ a.decodeError='grande'; return a; }
// JPG and WebP cannot be read in parts: the art is placed by its
// measurements, as a full box, and the team checks it before printing.
if(!/\.png$/i.test(a.f.name)){ a.semPrevia=true; return a; }
a.lendo=0; pintaArtes();
let mostrado=0;
const cv=await previaPngGrande(a.f, 1200, p=>{