Files
tracksteel_app/src/utils/pdfGenerator.ts
T

438 lines
13 KiB
TypeScript

import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
export const generateProfessionalPDF = async (elementId: string, filename: string) => {
try {
const element = document.getElementById(elementId);
if (!element) {
throw new Error('Elemento não encontrado para gerar PDF');
}
// Aguardar um pouco para garantir que o elemento esteja completamente renderizado
await new Promise(resolve => setTimeout(resolve, 200));
// Garantir que o elemento esteja visível e com dimensões corretas
const originalDisplay = element.style.display;
const originalVisibility = element.style.visibility;
const originalPosition = element.style.position;
element.style.display = 'block';
element.style.visibility = 'visible';
element.style.position = 'relative';
// Forçar um reflow
element.offsetHeight;
// Aguardar mais um pouco após forçar o reflow
await new Promise(resolve => setTimeout(resolve, 300));
console.log('Gerando PDF para elemento:', elementId);
console.log('Dimensões do elemento:', {
width: element.offsetWidth,
height: element.offsetHeight,
scrollWidth: element.scrollWidth,
scrollHeight: element.scrollHeight
});
// Se o elemento não tem dimensões, isso pode causar PDF em branco
if (element.offsetWidth === 0 || element.offsetHeight === 0) {
throw new Error('Elemento tem dimensões zero - não é possível gerar PDF');
}
// Configurações otimizadas para html2canvas
const canvas = await html2canvas(element, {
scale: 2,
useCORS: true,
allowTaint: false,
backgroundColor: '#ffffff',
width: element.scrollWidth,
height: element.scrollHeight,
scrollX: 0,
scrollY: 0,
windowWidth: Math.max(element.scrollWidth, 1200),
windowHeight: Math.max(element.scrollHeight, 800),
foreignObjectRendering: false,
removeContainer: false,
imageTimeout: 10000,
logging: false
});
console.log('Canvas criado com sucesso:', {
width: canvas.width,
height: canvas.height
});
// Verificar se o canvas foi criado corretamente
if (canvas.width === 0 || canvas.height === 0) {
throw new Error('Canvas criado com dimensões zero');
}
// Restaurar estilos originais
element.style.display = originalDisplay;
element.style.visibility = originalVisibility;
element.style.position = originalPosition;
// Criar PDF com configurações otimizadas
const pdf = new jsPDF({
orientation: 'portrait',
unit: 'mm',
format: 'a4',
compress: true
});
// Dimensões A4 em mm
const pageWidth = 210;
const pageHeight = 297;
// Margens adequadas
const marginTop = 15;
const marginBottom = 15;
const marginLeft = 15;
const marginRight = 15;
// Área útil para conteúdo
const contentWidth = pageWidth - marginLeft - marginRight;
const contentHeight = pageHeight - marginTop - marginBottom;
// Calcular proporções mantendo aspect ratio
const imgWidth = contentWidth;
const imgHeight = (canvas.height * contentWidth) / canvas.width;
// Converter canvas para imagem
const imgData = canvas.toDataURL('image/png', 1.0);
console.log('Adicionando imagem ao PDF:', {
imgWidth,
imgHeight,
contentHeight,
totalPages: Math.ceil(imgHeight / contentHeight)
});
// Verificar se os dados da imagem foram gerados
if (!imgData || imgData === 'data:,') {
throw new Error('Falha ao gerar dados da imagem do canvas');
}
// Sistema de paginação
let currentY = 0;
let pageNumber = 1;
const totalPages = Math.ceil(imgHeight / contentHeight);
// Função para adicionar rodapé com numeração
const addFooter = (pageNum: number, totalPages: number) => {
pdf.setFontSize(8);
pdf.setTextColor(100, 100, 100);
const footerText = `Página ${pageNum} de ${totalPages}`;
const textWidth = pdf.getTextWidth(footerText);
const footerX = (pageWidth - textWidth) / 2;
const footerY = pageHeight - 8;
pdf.text(footerText, footerX, footerY);
};
// Primeira página
if (imgHeight <= contentHeight) {
// Conteúdo cabe em uma página
pdf.addImage(imgData, 'PNG', marginLeft, marginTop, imgWidth, imgHeight);
addFooter(1, 1);
} else {
// Conteúdo precisa de múltiplas páginas
while (currentY < imgHeight) {
if (pageNumber > 1) {
pdf.addPage();
}
// Calcular a altura restante do conteúdo
const remainingHeight = imgHeight - currentY;
const currentPageHeight = Math.min(contentHeight, remainingHeight);
// Criar um canvas temporário para a seção atual
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
if (tempCtx) {
tempCanvas.width = canvas.width;
tempCanvas.height = (currentPageHeight * canvas.width) / imgWidth;
// Desenhar a seção atual do canvas original
tempCtx.drawImage(
canvas,
0,
(currentY * canvas.width) / imgWidth,
canvas.width,
tempCanvas.height,
0,
0,
canvas.width,
tempCanvas.height
);
// Converter para dados de imagem
const tempImgData = tempCanvas.toDataURL('image/png', 1.0);
// Adicionar a seção ao PDF
pdf.addImage(tempImgData, 'PNG', marginLeft, marginTop, imgWidth, currentPageHeight);
}
// Adicionar rodapé
addFooter(pageNumber, totalPages);
// Preparar para próxima página
currentY += contentHeight;
pageNumber++;
}
}
// Forçar o download do PDF
console.log('Iniciando download do PDF:', filename);
pdf.save(filename);
// Aguardar um momento para garantir que o download seja iniciado
await new Promise(resolve => setTimeout(resolve, 500));
console.log('PDF salvo com sucesso:', filename);
return true;
} catch (error) {
console.error('Erro detalhado ao gerar PDF:', error);
throw new Error(`Erro ao gerar PDF: ${error.message}`);
}
};
export const printProfessionalPDF = async (elementId: string) => {
try {
const element = document.getElementById(elementId);
if (!element) {
throw new Error('Elemento não encontrado');
}
// Criar nova janela para impressão
const printWindow = window.open('', '_blank');
if (!printWindow) {
throw new Error('Não foi possível abrir janela de impressão');
}
// Obter estilos do documento atual
const styleSheets = Array.from(document.styleSheets);
let allStyles = '';
try {
styleSheets.forEach(sheet => {
try {
if (sheet.cssRules) {
Array.from(sheet.cssRules).forEach(rule => {
allStyles += rule.cssText + '\n';
});
}
} catch (e) {
// Ignorar erros de CORS
}
});
} catch (e) {
console.warn('Não foi possível obter alguns estilos', e);
}
// HTML otimizado para impressão sem áreas vazias
const printContent = `
<!DOCTYPE html>
<html>
<head>
<title>Relatório de Produção</title>
<meta charset="UTF-8">
<style>
${allStyles}
/* Reset e configuração básica */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@page {
size: A4;
margin: 15mm 20mm 15mm 20mm;
padding: 0;
}
@media print {
body {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
margin: 0 !important;
padding: 0 !important;
font-size: 10px !important;
line-height: 1.2 !important;
}
/* Remover elementos desnecessários */
.no-print {
display: none !important;
}
/* Otimizar espaçamento */
#${elementId} {
width: 100% !important;
max-width: none !important;
margin: 0 !important;
padding: 0 !important;
transform: none !important;
}
/* Compactar seções */
.statistics-grid {
margin-bottom: 8px !important;
gap: 4px !important;
}
.process-section {
margin-bottom: 10px !important;
page-break-inside: avoid;
}
.priority-section {
margin-bottom: 10px !important;
page-break-inside: avoid;
}
/* Otimizar tabelas */
table {
width: 100% !important;
border-collapse: collapse !important;
margin: 0 !important;
page-break-inside: avoid;
}
th, td {
padding: 2px 4px !important;
font-size: 9px !important;
line-height: 1.1 !important;
}
/* Compactar cabeçalhos */
h1 {
font-size: 16px !important;
margin-bottom: 4px !important;
line-height: 1.1 !important;
}
h2 {
font-size: 14px !important;
margin-bottom: 6px !important;
margin-top: 8px !important;
line-height: 1.1 !important;
}
h3 {
font-size: 12px !important;
margin-bottom: 4px !important;
line-height: 1.1 !important;
}
/* Reduzir espaçamentos desnecessários */
header {
margin-bottom: 8px !important;
padding-bottom: 8px !important;
}
section {
margin-bottom: 8px !important;
}
/* Otimizar cards de estatísticas */
.statistics-grid > div {
padding: 4px !important;
margin: 0 !important;
}
/* Evitar quebras desnecessárias */
.avoid-break {
page-break-inside: avoid !important;
}
.process-header {
page-break-after: avoid !important;
}
.priority-header {
page-break-after: avoid !important;
}
/* Compactar processos menores */
.process-section h3 {
padding: 3px 6px !important;
margin: 0 !important;
}
.process-section .grid {
gap: 3px !important;
padding: 3px !important;
margin-bottom: 3px !important;
}
/* Melhorar aproveitamento do espaço */
.overflow-x-auto {
overflow: visible !important;
}
/* Remover espaços extras */
p {
margin: 0 !important;
padding: 0 !important;
}
/* Compactar divs com display grid */
div[style*="display: grid"] {
gap: 3px !important;
}
/* Reduzir espaçamentos entre elementos */
div[style*="margin-bottom"] {
margin-bottom: 6px !important;
}
/* Otimizar áreas de processo */
.process-section > div:last-child {
margin-bottom: 0 !important;
}
/* Compactar mais os elementos */
div[style*="padding: 10px"] {
padding: 4px !important;
}
div[style*="padding: 8px"] {
padding: 3px !important;
}
div[style*="padding: 6px"] {
padding: 2px !important;
}
}
</style>
</head>
<body>
${element.outerHTML}
<script>
window.onload = function() {
// Aguardar renderização e depois imprimir
setTimeout(() => {
window.print();
window.close();
}, 800);
}
</script>
</body>
</html>
`;
printWindow.document.write(printContent);
printWindow.document.close();
return true;
} catch (error) {
console.error('Erro ao imprimir PDF:', error);
throw error;
}
};