tenta fix pdfservice.js
This commit is contained in:
1 parent
4c4591fee5
commit
6d487b5834
1 file changed
+107
-125
+107
-125
@@ -204,10 +204,9 @@ export class PdfService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gera um PDF a partir do conteúdo HTML e dados do escritório
|
* Gera um PDF a partir do conteúdo HTML simples (apenas texto) e dados do escritório,
|
||||||
* @param conteudo - Conteúdo HTML para o PDF
|
* encaixando o texto sobre o timbrado (PDF de fundo) em todas as páginas.
|
||||||
* @param escritorio - Dados do escritório para substituir no template
|
* Não usa Puppeteer, apenas PDF-LIB.
|
||||||
* @returns Buffer contendo o PDF gerado
|
|
||||||
*/
|
*/
|
||||||
async gerarPDF(conteudo: string, escritorio: any = {}): Promise<Buffer> {
|
async gerarPDF(conteudo: string, escritorio: any = {}): Promise<Buffer> {
|
||||||
try {
|
try {
|
||||||
@@ -216,10 +215,6 @@ export class PdfService {
|
|||||||
|
|
||||||
// Verificar se temos um timbrado para usar
|
// Verificar se temos um timbrado para usar
|
||||||
const temTimbrado = !!config.timbradoUrl;
|
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 timbradoPath = '';
|
||||||
let timbradoExists = false;
|
let timbradoExists = false;
|
||||||
|
|
||||||
@@ -229,19 +224,15 @@ export class PdfService {
|
|||||||
} else {
|
} else {
|
||||||
timbradoPath = path.join(process.cwd(), 'public', 'uploads', path.basename(config.timbradoUrl));
|
timbradoPath = path.join(process.cwd(), 'public', 'uploads', path.basename(config.timbradoUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Verificando timbrado em:', timbradoPath);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.access(timbradoPath);
|
await fs.access(timbradoPath);
|
||||||
timbradoExists = true;
|
timbradoExists = true;
|
||||||
console.log('Timbrado encontrado');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Timbrado não encontrado:', e);
|
timbradoExists = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Margens definidas no template ou padrão
|
// Margens
|
||||||
const margens = config.margensTemplate || {
|
const margens = config.margensTemplate || {
|
||||||
top: config.timbradoMarginTop || 100,
|
top: config.timbradoMarginTop || 100,
|
||||||
left: config.timbradoMarginLeft || 50,
|
left: config.timbradoMarginLeft || 50,
|
||||||
@@ -249,123 +240,114 @@ export class PdfService {
|
|||||||
bottom: config.timbradoMarginBottom || 50
|
bottom: config.timbradoMarginBottom || 50
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Usando margens:', margens);
|
// 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;
|
||||||
|
|
||||||
/**
|
// Processar variáveis do template
|
||||||
* Gera o PDF final combinando o timbrado com o conteúdo
|
const texto = this.processarTemplate(conteudo, escritorio)
|
||||||
*/
|
.replace(/<[^>]+>/g, '') // Remove tags HTML
|
||||||
const gerarPdfComTimbrado = async (
|
.replace(/ /g, ' ')
|
||||||
content: string,
|
.replace(/&/g, '&')
|
||||||
pageW: number,
|
.replace(/</g, '<')
|
||||||
pageH: number,
|
.replace(/>/g, '>')
|
||||||
margins: any,
|
.replace(/"/g, '"')
|
||||||
usesTimbrado: boolean,
|
.replace(/'/g, "'")
|
||||||
timbradoFilePath: string
|
.replace(/\r\n/g, '\n')
|
||||||
): Promise<Buffer> => {
|
.replace(/\r/g, '\n')
|
||||||
console.log('Gerando PDF com timbrado...');
|
.replace(/\n{3,}/g, '\n\n')
|
||||||
|
.trim();
|
||||||
|
|
||||||
const browser = await puppeteer.launch({
|
// Quebra o texto em parágrafos
|
||||||
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
const paragrafos = texto.split(/\n{2,}/).map(p => p.trim()).filter(Boolean);
|
||||||
headless: true
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentLine) lines.push(currentLine);
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
try {
|
currentLine = 0;
|
||||||
// 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>
|
if (lineIdx < lines.length) {
|
||||||
</head>
|
// Não coube tudo, continuar na próxima página
|
||||||
<body>${content}</body>
|
break;
|
||||||
</html>
|
}
|
||||||
`, { waitUntil: 'networkidle0' });
|
currentPar++;
|
||||||
|
}
|
||||||
const contentPdfBuffer = await page.pdf({
|
pageIndex++;
|
||||||
format: 'A4',
|
pages.push(page);
|
||||||
printBackground: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!usesTimbrado) {
|
|
||||||
return contentPdfBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const pdfBytes = await pdfDoc.save();
|
||||||
// Carregar os PDFs
|
return Buffer.from(pdfBytes);
|
||||||
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erro geral ao gerar PDF:', error);
|
console.error('Erro geral ao gerar PDF:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
Reference in new issue
Block a user