tenta fix pdfservice.js
This commit is contained in:
1 parent
4c4591fee5
commit
6d487b5834
1 file changed
+110
-128
+110
-128
@@ -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(/ /g, ' ')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/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;
|
||||
|
||||
Reference in new issue
Block a user