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
|
||||
const getBase64Image = async (imgUrl) => {
|
||||
// Helper para converter URL de imagem local em base64 e obter dimensões reais sem deformar
|
||||
const getBase64ImageDetails = async (imgUrl) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'Anonymous';
|
||||
img.onload = () => {
|
||||
const width = img.naturalWidth || img.width || 1280;
|
||||
const height = img.naturalHeight || img.height || 720;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
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.src = imgUrl;
|
||||
});
|
||||
};
|
||||
|
||||
const getBase64Image = async (imgUrl) => {
|
||||
const details = await getBase64ImageDetails(imgUrl);
|
||||
return details.dataUrl;
|
||||
};
|
||||
|
||||
// Helper para execução concorrente controlada
|
||||
const mapConcurrent = async (items, limit, fn) => {
|
||||
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.setFontSize(18);
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.text(title, 297, 50, { align: 'center' });
|
||||
doc.text(title, 297, 45, { align: 'center' });
|
||||
|
||||
// Imagem
|
||||
const base64Img = await getBase64Image(panel.imageUrl);
|
||||
const imgWidth = 515;
|
||||
const imgHeight = selectedComicsRatio === '16:9' ? 290 : 386;
|
||||
doc.addImage(base64Img, 'JPEG', 40, 80, imgWidth, imgHeight);
|
||||
const imgDetails = await getBase64ImageDetails(panel.imageUrl);
|
||||
const boxWidth = 515;
|
||||
const boxMaxHeight = 350;
|
||||
const textHeight = 150;
|
||||
|
||||
// Altura esticada para a caixa de diálogo (160pt de altura - aprox dobro)
|
||||
const textHeight = 160;
|
||||
const textY = 80 + imgHeight;
|
||||
// Cálculo exato mantendo a proporção real da imagem (sem esticar)
|
||||
let renderW = boxWidth;
|
||||
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.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.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);
|
||||
|
||||
// Texto maior e mais legível (dobro do tamanho relativo - 15pt)
|
||||
// Texto legível
|
||||
if (panel.dialogue) {
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(15);
|
||||
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);
|
||||
}
|
||||
|
||||
// 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.circle(65, 105, 12, 'F');
|
||||
doc.circle(65, 95, 12, 'F');
|
||||
doc.setDrawColor(30, 41, 59);
|
||||
doc.setLineWidth(1.5);
|
||||
doc.circle(65, 105, 12, 'D');
|
||||
doc.circle(65, 95, 12, 'D');
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(10);
|
||||
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é
|
||||
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++) {
|
||||
if (page > 0) doc.addPage();
|
||||
|
||||
// Título centralizado no topo da página paisagem
|
||||
// Título centralizado no topo
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(18);
|
||||
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 startX = col === 0 ? 40 : 426;
|
||||
const cardWidth = 376;
|
||||
const boxWidth = 376;
|
||||
const boxMaxHeight = 230;
|
||||
const textHeight = 140;
|
||||
|
||||
// Proporções exatas mantidas
|
||||
const imgHeight = selectedComicsRatio === '16:9' ? 211 : 282;
|
||||
const textHeight = 140; // Dobro de tamanho (antes era 65)
|
||||
const imgDetails = await getBase64ImageDetails(panel.imageUrl);
|
||||
|
||||
// Imagem do quadrinho
|
||||
const base64Img = await getBase64Image(panel.imageUrl);
|
||||
doc.addImage(base64Img, 'JPEG', startX, 70, cardWidth, imgHeight);
|
||||
// Cálculo rigoroso da dimensão sem deformar (preserva aspect ratio real)
|
||||
let renderW = boxWidth;
|
||||
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
|
||||
const textY = 70 + imgHeight;
|
||||
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)
|
||||
doc.setDrawColor(30, 41, 59);
|
||||
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
|
||||
doc.line(startX, textY, startX + cardWidth, textY);
|
||||
// Linha separadora entre imagem e caixa de texto
|
||||
doc.line(startX, textY, startX + boxWidth, textY);
|
||||
|
||||
// Legenda formatada em tamanho maior (13pt)
|
||||
// Legenda formatada
|
||||
if (panel.dialogue) {
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(13);
|
||||
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);
|
||||
}
|
||||
|
||||
// Círculo discreto com o numeral do quadro no canto superior esquerdo da imagem
|
||||
// Numeral do quadro
|
||||
doc.setFillColor(254, 240, 138);
|
||||
doc.circle(startX + 20, 90, 11, 'F');
|
||||
doc.setDrawColor(30, 41, 59);
|
||||
|
||||
Reference in New Issue
Block a user