tenta fix pdfservice.js

This commit is contained in:
rodrigo committed 2025-04-21 19:31:33 -03:00
1 parent 4c4591fee5
commit 6d487b5834
1 file changed
+110 -128
+110 -128
View File
@@ -204,168 +204,150 @@ export class PdfService {
}
/**
* Gera um PDF a partir do conteúdo HTML e dados do escritório
* @param conteudo - Conteúdo HTML para o PDF
* @param escritorio - Dados do escritório para substituir no template
* @returns Buffer contendo o PDF gerado
* Gera um PDF a partir do conteúdo HTML simples (apenas texto) e dados do escritório,
* encaixando o texto sobre o timbrado (PDF de fundo) em todas as páginas.
* Não usa Puppeteer, apenas PDF-LIB.
*/
async gerarPDF(conteudo: string, escritorio: any = {}): Promise<Buffer> {
try {
console.log('Buscando configurações do documento...');
const config = await this.getConfiguracao();
// Verificar se temos um timbrado para usar
const temTimbrado = !!config.timbradoUrl;
console.log(`Iniciando geração de PDF ${temTimbrado ? 'com' : 'sem'} timbrado...`);
// Se temos timbrado, vamos primeiro verificar se ele existe
let timbradoPath = '';
let timbradoExists = false;
if (temTimbrado) {
if (config.timbradoUrl.startsWith('/uploads')) {
timbradoPath = path.join(process.cwd(), 'public', config.timbradoUrl);
} else {
timbradoPath = path.join(process.cwd(), 'public', 'uploads', path.basename(config.timbradoUrl));
}
console.log('Verificando timbrado em:', timbradoPath);
try {
await fs.access(timbradoPath);
timbradoExists = true;
console.log('Timbrado encontrado');
} catch (e) {
console.error('Timbrado não encontrado:', e);
timbradoExists = false;
}
}
// Margens definidas no template ou padrão
// Margens
const margens = config.margensTemplate || {
top: config.timbradoMarginTop || 100,
left: config.timbradoMarginLeft || 50,
right: config.timbradoMarginRight || 50,
bottom: config.timbradoMarginBottom || 50
};
console.log('Usando margens:', margens);
/**
* Gera o PDF final combinando o timbrado com o conteúdo
*/
const gerarPdfComTimbrado = async (
content: string,
pageW: number,
pageH: number,
margins: any,
usesTimbrado: boolean,
timbradoFilePath: string
): Promise<Buffer> => {
console.log('Gerando PDF com timbrado...');
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
headless: true
});
// Parâmetros de página
const PAGE_WIDTH = 595.28; // A4
const PAGE_HEIGHT = 841.89; // A4
const FONT_SIZE = 12;
const LINE_HEIGHT = FONT_SIZE * 1.5;
const MAX_WIDTH = PAGE_WIDTH - margens.left - margens.right;
try {
// Gerar primeiro o PDF do conteúdo
const page = await browser.newPage();
await page.setContent(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
@page { size: A4; margin: 0; }
body {
font-family: Arial, sans-serif;
font-size: 11pt;
line-height: 1.5;
padding: ${margins.top}px ${margins.right}px ${margins.bottom}px ${margins.left}px;
text-align: justify;
}
</style>
</head>
<body>${content}</body>
</html>
`, { waitUntil: 'networkidle0' });
// Processar variáveis do template
const texto = this.processarTemplate(conteudo, escritorio)
.replace(/<[^>]+>/g, '') // Remove tags HTML
.replace(/&nbsp;/g, ' ')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.replace(/\n{3,}/g, '\n\n')
.trim();
const contentPdfBuffer = await page.pdf({
format: 'A4',
printBackground: true
});
// Quebra o texto em parágrafos
const paragrafos = texto.split(/\n{2,}/).map(p => p.trim()).filter(Boolean);
if (!usesTimbrado) {
return contentPdfBuffer;
// Função para quebrar texto em linhas respeitando largura máxima
function wrapText(text: string, font: any, fontSize: number, maxWidth: number): string[] {
const words = text.split(' ');
const lines: string[] = [];
let currentLine = '';
for (const word of words) {
const testLine = currentLine ? currentLine + ' ' + word : word;
const width = font.widthOfTextAtSize(testLine, fontSize);
if (width > maxWidth && currentLine) {
lines.push(currentLine);
currentLine = word;
} else {
currentLine = testLine;
}
try {
// Carregar os PDFs
const timbradoPdf = await PDFDocument.load(await fs.readFile(timbradoFilePath));
const contentPdf = await PDFDocument.load(contentPdfBuffer);
// Criar documento final
const finalPdf = await PDFDocument.create();
// Obter a primeira página do timbrado como template
const [timbradoPage] = await finalPdf.copyPages(timbradoPdf, [0]);
const contentPages = await finalPdf.copyPages(contentPdf, contentPdf.getPageIndices());
// Para cada página do conteúdo
for (const contentPage of contentPages) {
// 1. Adicionar nova página
const newPage = finalPdf.addPage([pageW, pageH]);
// 2. Copiar conteúdo do timbrado para a nova página
try {
newPage.drawPage(timbradoPage, {
x: 0,
y: 0,
width: pageW,
height: pageH
});
// 3. Copiar o conteúdo sobre o timbrado
newPage.drawPage(contentPage, {
x: margins.left,
y: margins.bottom,
width: pageW - margins.left - margins.right,
height: pageH - margins.top - margins.bottom
});
} catch (pageError) {
console.error('Erro ao processar página:', pageError);
}
}
// Salvar o documento final
const finalPdfBytes = await finalPdf.save();
return Buffer.from(finalPdfBytes);
} catch (mergeError) {
console.error('Erro ao mesclar PDFs:', mergeError);
return contentPdfBuffer; // Retorna conteúdo original em caso de erro
}
} finally {
await browser.close();
}
};
if (currentLine) lines.push(currentLine);
return lines;
}
// Processar o conteúdo e gerar o PDF
const processedContent = this.processarTemplate(conteudo, escritorio);
const pageWidth = 595;
const pageHeight = 842;
const pdfBytes = await gerarPdfComTimbrado(
processedContent,
pageWidth,
pageHeight,
margens,
timbradoExists,
timbradoPath
);
console.log(`PDF gerado com sucesso: ${pdfBytes.length} bytes`);
return pdfBytes;
// Carregar o timbrado (se houver) e criar o PDF final
let pdfDoc: PDFDocument;
let timbradoPages: any[] = [];
if (timbradoExists) {
const timbradoBytes = await fs.readFile(timbradoPath);
const timbradoPdf = await PDFDocument.load(timbradoBytes);
pdfDoc = await PDFDocument.create();
timbradoPages = await pdfDoc.copyPages(timbradoPdf, Array.from({length: timbradoPdf.getPageCount()}, (_, i) => i));
} else {
pdfDoc = await PDFDocument.create();
}
// Fonte
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
// Quebrar o texto em páginas
let currentPar = 0;
let currentLine = 0;
let pageIndex = 0;
const pages: any[] = [];
while (currentPar < paragrafos.length) {
// Adicionar página do timbrado ou em branco
let page;
if (timbradoPages.length > 0 && pageIndex < timbradoPages.length) {
page = pdfDoc.addPage(timbradoPages[pageIndex]);
} else if (timbradoPages.length > 0) {
// Se o timbrado tem só 1 página, repetir
page = pdfDoc.addPage(timbradoPages[0]);
} else {
page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]);
}
let y = PAGE_HEIGHT - margens.top;
// Escrever texto na página
while (currentPar < paragrafos.length) {
const lines = wrapText(paragrafos[currentPar], font, FONT_SIZE, MAX_WIDTH);
let lineIdx = 0;
if (currentLine > 0) lineIdx = currentLine;
for (; lineIdx < lines.length; lineIdx++) {
if (y < margens.bottom + LINE_HEIGHT) {
// Próxima página
currentLine = lineIdx;
break;
}
page.drawText(lines[lineIdx], {
x: margens.left,
y,
size: FONT_SIZE,
font
});
y -= LINE_HEIGHT;
currentLine = 0;
}
if (lineIdx < lines.length) {
// Não coube tudo, continuar na próxima página
break;
}
currentPar++;
}
pageIndex++;
pages.push(page);
}
const pdfBytes = await pdfDoc.save();
return Buffer.from(pdfBytes);
} catch (error) {
console.error('Erro geral ao gerar PDF:', error);
throw error;