Garante proporcao exata e sem deformacao das imagens nas exportacoes de PDF de quadrinhos (Retrato e Paisagem)
This commit is contained in:
+75
-41
@@ -4899,24 +4899,36 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Helper para converter URL de imagem local em base64
|
// Helper para converter URL de imagem local em base64 e obter dimensões reais sem deformar
|
||||||
const getBase64Image = async (imgUrl) => {
|
const getBase64ImageDetails = async (imgUrl) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.crossOrigin = 'Anonymous';
|
img.crossOrigin = 'Anonymous';
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
|
const width = img.naturalWidth || img.width || 1280;
|
||||||
|
const height = img.naturalHeight || img.height || 720;
|
||||||
const canvas = document.createElement('canvas');
|
const canvas = document.createElement('canvas');
|
||||||
canvas.width = img.width;
|
canvas.width = width;
|
||||||
canvas.height = img.height;
|
canvas.height = height;
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
ctx.drawImage(img, 0, 0);
|
ctx.drawImage(img, 0, 0);
|
||||||
resolve(canvas.toDataURL('image/jpeg'));
|
resolve({
|
||||||
|
dataUrl: canvas.toDataURL('image/jpeg'),
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
aspectRatio: width / height
|
||||||
|
});
|
||||||
};
|
};
|
||||||
img.onerror = (e) => reject(new Error('Falha ao carregar imagem para o PDF: ' + imgUrl));
|
img.onerror = (e) => reject(new Error('Falha ao carregar imagem para o PDF: ' + imgUrl));
|
||||||
img.src = imgUrl;
|
img.src = imgUrl;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getBase64Image = async (imgUrl) => {
|
||||||
|
const details = await getBase64ImageDetails(imgUrl);
|
||||||
|
return details.dataUrl;
|
||||||
|
};
|
||||||
|
|
||||||
// Helper para execução concorrente controlada
|
// Helper para execução concorrente controlada
|
||||||
const mapConcurrent = async (items, limit, fn) => {
|
const mapConcurrent = async (items, limit, fn) => {
|
||||||
const results = new Array(items.length);
|
const results = new Array(items.length);
|
||||||
@@ -5444,51 +5456,62 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(18);
|
doc.setFontSize(18);
|
||||||
doc.setTextColor(30, 41, 59);
|
doc.setTextColor(30, 41, 59);
|
||||||
doc.text(title, 297, 50, { align: 'center' });
|
doc.text(title, 297, 45, { align: 'center' });
|
||||||
|
|
||||||
// Imagem
|
const imgDetails = await getBase64ImageDetails(panel.imageUrl);
|
||||||
const base64Img = await getBase64Image(panel.imageUrl);
|
const boxWidth = 515;
|
||||||
const imgWidth = 515;
|
const boxMaxHeight = 350;
|
||||||
const imgHeight = selectedComicsRatio === '16:9' ? 290 : 386;
|
const textHeight = 150;
|
||||||
doc.addImage(base64Img, 'JPEG', 40, 80, imgWidth, imgHeight);
|
|
||||||
|
|
||||||
// Altura esticada para a caixa de diálogo (160pt de altura - aprox dobro)
|
// Cálculo exato mantendo a proporção real da imagem (sem esticar)
|
||||||
const textHeight = 160;
|
let renderW = boxWidth;
|
||||||
const textY = 80 + imgHeight;
|
let renderH = renderW / imgDetails.aspectRatio;
|
||||||
|
if (renderH > boxMaxHeight) {
|
||||||
|
renderH = boxMaxHeight;
|
||||||
|
renderW = renderH * imgDetails.aspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
// Caixa de diálogo com fundo cinza claro e borda integrada
|
const imgX = 40 + (boxWidth - renderW) / 2;
|
||||||
|
const imgY = 70 + (boxMaxHeight - renderH) / 2;
|
||||||
|
const cardY = 70;
|
||||||
|
const textY = cardY + boxMaxHeight;
|
||||||
|
|
||||||
|
// Desenha a imagem centralizada e proporcional
|
||||||
|
doc.addImage(imgDetails.dataUrl, 'JPEG', imgX, imgY, renderW, renderH);
|
||||||
|
|
||||||
|
// Fundo da caixa de texto
|
||||||
doc.setFillColor(248, 250, 252);
|
doc.setFillColor(248, 250, 252);
|
||||||
doc.rect(40, textY, imgWidth, textHeight, 'F');
|
doc.rect(40, textY, boxWidth, textHeight, 'F');
|
||||||
|
|
||||||
// Moldura unificada ao redor de toda a imagem + texto
|
// Moldura unificada ao redor de todo o quadro
|
||||||
doc.setDrawColor(30, 41, 59);
|
doc.setDrawColor(30, 41, 59);
|
||||||
doc.setLineWidth(2);
|
doc.setLineWidth(2);
|
||||||
doc.rect(40, 80, imgWidth, imgHeight + textHeight, 'D');
|
doc.rect(40, cardY, boxWidth, boxMaxHeight + textHeight, 'D');
|
||||||
|
|
||||||
// Linha divisória entre a imagem e a caixa de texto
|
// Linha divisória entre imagem e texto
|
||||||
doc.line(40, textY, 555, textY);
|
doc.line(40, textY, 555, textY);
|
||||||
|
|
||||||
// Texto maior e mais legível (dobro do tamanho relativo - 15pt)
|
// Texto legível
|
||||||
if (panel.dialogue) {
|
if (panel.dialogue) {
|
||||||
doc.setFont('helvetica', 'normal');
|
doc.setFont('helvetica', 'normal');
|
||||||
doc.setFontSize(15);
|
doc.setFontSize(15);
|
||||||
doc.setTextColor(15, 23, 42);
|
doc.setTextColor(15, 23, 42);
|
||||||
|
|
||||||
const splitText = doc.splitTextToSize(panel.dialogue, imgWidth - 40);
|
const splitText = doc.splitTextToSize(panel.dialogue, boxWidth - 40);
|
||||||
doc.text(splitText, 60, textY + 35);
|
doc.text(splitText, 60, textY + 35);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Círculo discreto com o número do quadrinho no canto superior esquerdo da imagem
|
// Numeração do quadrinho
|
||||||
doc.setFillColor(254, 240, 138);
|
doc.setFillColor(254, 240, 138);
|
||||||
doc.circle(65, 105, 12, 'F');
|
doc.circle(65, 95, 12, 'F');
|
||||||
doc.setDrawColor(30, 41, 59);
|
doc.setDrawColor(30, 41, 59);
|
||||||
doc.setLineWidth(1.5);
|
doc.setLineWidth(1.5);
|
||||||
doc.circle(65, 105, 12, 'D');
|
doc.circle(65, 95, 12, 'D');
|
||||||
|
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(10);
|
doc.setFontSize(10);
|
||||||
doc.setTextColor(30, 41, 59);
|
doc.setTextColor(30, 41, 59);
|
||||||
doc.text(String(panel.panel_number), 65, 109, { align: 'center' });
|
doc.text(String(panel.panel_number), 65, 99, { align: 'center' });
|
||||||
|
|
||||||
// Rodapé
|
// Rodapé
|
||||||
doc.setFont('helvetica', 'italic');
|
doc.setFont('helvetica', 'italic');
|
||||||
@@ -5526,7 +5549,7 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
for (let page = 0; page < totalPages; page++) {
|
for (let page = 0; page < totalPages; page++) {
|
||||||
if (page > 0) doc.addPage();
|
if (page > 0) doc.addPage();
|
||||||
|
|
||||||
// Título centralizado no topo da página paisagem
|
// Título centralizado no topo
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(18);
|
doc.setFontSize(18);
|
||||||
doc.setTextColor(30, 41, 59);
|
doc.setTextColor(30, 41, 59);
|
||||||
@@ -5539,40 +5562,51 @@ Se algum dado não for mencionado, deixe a string vazia ou false para boolean.`
|
|||||||
|
|
||||||
const panel = generatedPanelsData[index];
|
const panel = generatedPanelsData[index];
|
||||||
const startX = col === 0 ? 40 : 426;
|
const startX = col === 0 ? 40 : 426;
|
||||||
const cardWidth = 376;
|
const boxWidth = 376;
|
||||||
|
const boxMaxHeight = 230;
|
||||||
|
const textHeight = 140;
|
||||||
|
|
||||||
// Proporções exatas mantidas
|
const imgDetails = await getBase64ImageDetails(panel.imageUrl);
|
||||||
const imgHeight = selectedComicsRatio === '16:9' ? 211 : 282;
|
|
||||||
const textHeight = 140; // Dobro de tamanho (antes era 65)
|
|
||||||
|
|
||||||
// Imagem do quadrinho
|
// Cálculo rigoroso da dimensão sem deformar (preserva aspect ratio real)
|
||||||
const base64Img = await getBase64Image(panel.imageUrl);
|
let renderW = boxWidth;
|
||||||
doc.addImage(base64Img, 'JPEG', startX, 70, cardWidth, imgHeight);
|
let renderH = renderW / imgDetails.aspectRatio;
|
||||||
|
if (renderH > boxMaxHeight) {
|
||||||
|
renderH = boxMaxHeight;
|
||||||
|
renderW = renderH * imgDetails.aspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imgX = startX + (boxWidth - renderW) / 2;
|
||||||
|
const imgY = 70 + (boxMaxHeight - renderH) / 2;
|
||||||
|
const cardY = 70;
|
||||||
|
const textY = cardY + boxMaxHeight;
|
||||||
|
|
||||||
|
// Imagem do quadrinho centralizada e mantendo proporções exatas
|
||||||
|
doc.addImage(imgDetails.dataUrl, 'JPEG', imgX, imgY, renderW, renderH);
|
||||||
|
|
||||||
// Fundo da caixa de texto
|
// Fundo da caixa de texto
|
||||||
const textY = 70 + imgHeight;
|
|
||||||
doc.setFillColor(248, 250, 252);
|
doc.setFillColor(248, 250, 252);
|
||||||
doc.rect(startX, textY, cardWidth, textHeight, 'F');
|
doc.rect(startX, textY, boxWidth, textHeight, 'F');
|
||||||
|
|
||||||
// Moldura unificada ao redor do quadro (imagem + texto)
|
// Moldura unificada ao redor do quadro (imagem + texto)
|
||||||
doc.setDrawColor(30, 41, 59);
|
doc.setDrawColor(30, 41, 59);
|
||||||
doc.setLineWidth(2);
|
doc.setLineWidth(2);
|
||||||
doc.rect(startX, 70, cardWidth, imgHeight + textHeight, 'D');
|
doc.rect(startX, cardY, boxWidth, boxMaxHeight + textHeight, 'D');
|
||||||
|
|
||||||
// Linha separando a imagem da caixa de texto
|
// Linha separadora entre imagem e caixa de texto
|
||||||
doc.line(startX, textY, startX + cardWidth, textY);
|
doc.line(startX, textY, startX + boxWidth, textY);
|
||||||
|
|
||||||
// Legenda formatada em tamanho maior (13pt)
|
// Legenda formatada
|
||||||
if (panel.dialogue) {
|
if (panel.dialogue) {
|
||||||
doc.setFont('helvetica', 'normal');
|
doc.setFont('helvetica', 'normal');
|
||||||
doc.setFontSize(13);
|
doc.setFontSize(13);
|
||||||
doc.setTextColor(15, 23, 42);
|
doc.setTextColor(15, 23, 42);
|
||||||
|
|
||||||
const splitText = doc.splitTextToSize(panel.dialogue, cardWidth - 30);
|
const splitText = doc.splitTextToSize(panel.dialogue, boxWidth - 30);
|
||||||
doc.text(splitText, startX + 15, textY + 30);
|
doc.text(splitText, startX + 15, textY + 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Círculo discreto com o numeral do quadro no canto superior esquerdo da imagem
|
// Numeral do quadro
|
||||||
doc.setFillColor(254, 240, 138);
|
doc.setFillColor(254, 240, 138);
|
||||||
doc.circle(startX + 20, 90, 11, 'F');
|
doc.circle(startX + 20, 90, 11, 'F');
|
||||||
doc.setDrawColor(30, 41, 59);
|
doc.setDrawColor(30, 41, 59);
|
||||||
|
|||||||
Reference in New Issue
Block a user