tenta fix pdfservice.js

This commit is contained in:
rodrigo committed 2025-04-22 12:14:27 -03:00
1 parent 49dba7c9be
commit 566ef2e123
1 file changed
+188 -187
+188 -187
View File
@@ -386,336 +386,337 @@ export class PdfService {
// Simplificar texto antes de processar para evitar loops infinitos // Simplificar texto antes de processar para evitar loops infinitos
// Remove tags não suportadas como <u>, <span>, etc. // Remove tags não suportadas como <u>, <span>, etc.
text = text text = text
.replace(/<u>|<\/u>/gi, '') // Remover tags de sublinhado .replace(/<u>|<\/u>/gi, '') // Remover tags de sublinhados de alinhamento que não foram processados
.replace(/<span[^>]*>|<\/span>/gi, '') // Remover tags span .replace(/<span[^>]*>|<\/span>/gi, '') // Remover tags span
.replace(/<[^>]*style=[^>]*>/gi, '') // Remover atributos style .replace(/<[^>]*style=[^>]*>/gi, '') // Remover atributos style
.replace(/<(?!\/?(?:b|i|strong|em)(?!\w)[^>]*>)[^>]*>/gi, ''); // Manter apenas b, i, strong, em .replace(/<(?!\/?(?:b|i|strong|em)(?!\w)[^>]*>)[^>]*>/gi, ''); // Manter apenas b, i, strong, em
.replace(/<(?!\/?(?:b|i|strong|em)(?!\w)[^>]*>)[^>]*>/gi, ''); // Manter apenas b, i, strong, em
// Primeiro, extrair partes do texto com suas formatações // Primeiro, extrair partes do texto com suas formatações
let i = 0; let i = 0;o, extrair partes do texto com suas formatações
while (i < text.length) { while (i < text.length) {
// Limite de segurança para evitar loops infinitos // Limite de segurança para evitar loops infinitos
if (textParts.length > 1000) { if (textParts.length > 1000) {itar loops infinitos
console.error('Limite de processamento de texto excedido'); console.error('Limite de processamento de texto excedido');
break; break;e.error('Limite de processamento de texto excedido');
} break;
} }
if (text.substring(i, i+3) === '<b>' || text.substring(i, i+7) === '<strong>') { if (text.substring(i, i+3) === '<b>' || text.substring(i, i+7) === '<strong>') {
boldActive = true; boldActive = true;, i+3) === '<b>' || text.substring(i, i+7) === '<strong>') {
currentFont = italicActive ? boldItalicFont : boldFont; currentFont = italicActive ? boldItalicFont : boldFont;
i += text.substring(i, i+3) === '<b>' ? 3 : 7; i += text.substring(i, i+3) === '<b>' ? 3 : 7;boldFont;
} } i += text.substring(i, i+3) === '<b>' ? 3 : 7;
else if (text.substring(i, i+4) === '</b>' || text.substring(i, i+8) === '</strong>') { else if (text.substring(i, i+4) === '</b>' || text.substring(i, i+8) === '</strong>') {
boldActive = false; boldActive = false;ng(i, i+4) === '</b>' || text.substring(i, i+8) === '</strong>') {
currentFont = italicActive ? italicFont : normalFont; currentFont = italicActive ? italicFont : normalFont;
i += text.substring(i, i+4) === '</b>' ? 4 : 8; i += text.substring(i, i+4) === '</b>' ? 4 : 8;lFont;
} } i += text.substring(i, i+4) === '</b>' ? 4 : 8;
else if (text.substring(i, i+3) === '<i>' || text.substring(i, i+4) === '<em>') { else if (text.substring(i, i+3) === '<i>' || text.substring(i, i+4) === '<em>') {
italicActive = true; italicActive = true;g(i, i+3) === '<i>' || text.substring(i, i+4) === '<em>') {
currentFont = boldActive ? boldItalicFont : italicFont; currentFont = boldActive ? boldItalicFont : italicFont;
i += text.substring(i, i+3) === '<i>' ? 3 : 4; i += text.substring(i, i+3) === '<i>' ? 3 : 4;alicFont;
} } i += text.substring(i, i+3) === '<i>' ? 3 : 4;
else if (text.substring(i, i+4) === '</i>' || text.substring(i, i+5) === '</em>') { else if (text.substring(i, i+4) === '</i>' || text.substring(i, i+5) === '</em>') {
italicActive = false; italicActive = false;(i, i+4) === '</i>' || text.substring(i, i+5) === '</em>') {
currentFont = boldActive ? boldFont : normalFont; currentFont = boldActive ? boldFont : normalFont;
i += text.substring(i, i+4) === '</i>' ? 4 : 5; i += text.substring(i, i+4) === '</i>' ? 4 : 5;t;
} } i += text.substring(i, i+4) === '</i>' ? 4 : 5;
else { else {
// Encontrar o próximo início de tag // Encontrar o próximo início de tag
let nextTagPos = text.indexOf('<', i); let nextTagPos = text.indexOf('<', i);
let nextTagPos = text.indexOf('<', i);
if (nextTagPos === -1) { if (nextTagPos === -1) {
// Não há mais tags, pegar o restante do texto // Não há mais tags, pegar o restante do texto
textParts.push({ textParts.push({ags, pegar o restante do texto
text: text.substring(i), text: text.substring(i),
font: currentFont font: currentFonting(i),
}); });ont: currentFont
break; break;
} else { } else {
// Pegar texto até a próxima tag // Pegar texto até a próxima tag
textParts.push({ textParts.push({té a próxima tag
text: text.substring(i, nextTagPos), text: text.substring(i, nextTagPos),
font: currentFont font: currentFonting(i, nextTagPos),
}); });ont: currentFont
i = nextTagPos; i = nextTagPos;
} i = nextTagPos;
} }
} }
} }
}
}
// Processa partes de texto em palavras para quebra de linha // Processa partes de texto em palavras para quebra de linha
for (const part of textParts) { for (const part of textParts) {palavras para quebra de linha
const partWords = part.text.split(/\s+/);
const partWords = part.text.split(/\s+/); const partWords = part.text.split(/\s+/);
for (let i = 0; i < partWords.length; i++) { for (let i = 0; i < partWords.length; i++) {
if (partWords[i].length === 0) continue; {
if (partWords[i].length === 0) continue; if (partWords[i].length === 0) continue;
const wordText = i < partWords.length - 1 ? partWords[i] + ' ' : partWords[i]; const wordText = i < partWords.length - 1 ? partWords[i] + ' ' : partWords[i];
const wordWidth = part.font.widthOfTextAtSize(wordText, fontSize);artWords[i];
const wordWidth = part.font.widthOfTextAtSize(wordText, fontSize); const wordWidth = part.font.widthOfTextAtSize(wordText, fontSize);
words.push({ words.push({
text: wordText, text: wordText,
font: part.font, font: part.font,
width: wordWidth width: wordWidth
}); });idth: wordWidth
} });
} }
} }
}
// Processa palavras em linhas // Processa palavras em linhas
for (let i = 0; i < words.length; i++) { for (let i = 0; i < words.length; i++) {
const word = words[i];s.length; i++) {
const word = words[i]; const word = words[i];
if (currentLineWidth + word.width <= maxWidth) { if (currentLineWidth + word.width <= maxWidth) {
// A palavra cabe na linha atual // A palavra cabe na linha atual<= maxWidth) {
currentLine.push(word); currentLine.push(word);nha atual
currentLineWidth += word.width; currentLineWidth += word.width;
} else { } else {tLineWidth += word.width;
// A palavra não cabe, desenha a linha atual e começa nova linha // A palavra não cabe, desenha a linha atual e começa nova linha
// Primeiro verifica se há espaço vertical suficiente // Primeiro verifica se há espaço vertical suficiente nova linha
if (y - lineHeight < marginBottom) { if (y - lineHeight < marginBottom) {rtical suficiente
return null; // Não há espaço suficiente na página return null; // Não há espaço suficiente na página
} return null; // Não há espaço suficiente na página
} }
// Desenha a linha com o alinhamento apropriado // Desenha a linha com o alinhamento apropriado
y = drawFormattedLine(page, currentLine, x, y, maxWidth, align, fontSize, color); y = drawFormattedLine(page, currentLine, x, y, maxWidth, align, fontSize, color);
y = drawFormattedLine(page, currentLine, x, y, maxWidth, align, fontSize, color);
// Começa uma nova linha com a palavra atual // Começa uma nova linha com a palavra atual
currentLine = [word]; currentLine = [word];nha com a palavra atual
currentLineWidth = word.width; currentLineWidth = word.width;
} currentLineWidth = word.width;
} }
} }
}
// Desenha a última linha se houver palavras restantes // Desenha a última linha se houver palavras restantes
if (currentLine.length > 0) { if (currentLine.length > 0) {houver palavras restantes
if (y - lineHeight < marginBottom) { if (y - lineHeight < marginBottom) {
return null; // Não há espaço suficiente na página return null; // Não há espaço suficiente na página
} return null; // Não há espaço suficiente na página
} }
y = drawFormattedLine(page, currentLine, x, y, maxWidth, align, fontSize, color); y = drawFormattedLine(page, currentLine, x, y, maxWidth, align, fontSize, color);
} y = drawFormattedLine(page, currentLine, x, y, maxWidth, align, fontSize, color);
} }
return y; return y;
} return y;
} }
/** /**
* Desenha uma linha formatada com alinhamento específico * Desenha uma linha formatada com alinhamento específico
*/ */Desenha uma linha formatada com alinhamento específico
function drawFormattedLine( function drawFormattedLine(
page: any, page: any, FormattedLine(
line: Array<{text: string, font: any, width: number}>, line: Array<{text: string, font: any, width: number}>,
x: number, x: number,y<{text: string, font: any, width: number}>,
y: number, y: number,
maxWidth: number, maxWidth: number,
align: 'left' | 'center' | 'right' | 'justify', align: 'left' | 'center' | 'right' | 'justify',
fontSize: number, fontSize: number,center' | 'right' | 'justify',
color: any color: anynumber,
): number { ): number {y
const lineHeight = fontSize * 1.5; const lineHeight = fontSize * 1.5;
let currentX = x; let currentX = x;= fontSize * 1.5;
let totalWidth = line.reduce((sum, word) => sum + word.width, 0);
let totalWidth = line.reduce((sum, word) => sum + word.width, 0); let totalWidth = line.reduce((sum, word) => sum + word.width, 0);
// Calcula o espaço adicional entre palavras para justificação // Calcula o espaço adicional entre palavras para justificação
let extraSpacing = 0; let extraSpacing = 0;dicional entre palavras para justificação
if (align === 'justify' && line.length > 1 && totalWidth < maxWidth * 0.8) { if (align === 'justify' && line.length > 1 && totalWidth < maxWidth * 0.8) {
extraSpacing = (maxWidth - totalWidth) / (line.length - 1); extraSpacing = (maxWidth - totalWidth) / (line.length - 1);xWidth * 0.8) {
} extraSpacing = (maxWidth - totalWidth) / (line.length - 1);
} }
// Ajusta a posição x inicial baseada no alinhamento // Ajusta a posição x inicial baseada no alinhamento
if (align === 'center') { if (align === 'center') {cial baseada no alinhamento
currentX = x + (maxWidth - totalWidth) / 2; currentX = x + (maxWidth - totalWidth) / 2;
} else if (align === 'right') { } else if (align === 'right') {talWidth) / 2;
currentX = x + maxWidth - totalWidth; currentX = x + maxWidth - totalWidth;
} currentX = x + maxWidth - totalWidth;
} }
// Desenha cada palavra // Desenha cada palavra
for (let i = 0; i < line.length; i++) { for (let i = 0; i < line.length; i++) {
const word = line[i]; const word = line[i];e.length; i++) {
page.drawText(word.text, { page.drawText(word.text, {
x: currentX, x: currentX,word.text, {
y: y, y: y,rrentX,
font: word.font, font: word.font,
size: fontSize, size: fontSize,,
color: color color: colorze,
});olor: color
}); });
// Avança X para a próxima palavra // Avança X para a próxima palavra
currentX += word.width;ima palavra
currentX += word.width; currentX += word.width;
// Adiciona espaço extra para justificação, exceto na última palavra // Adiciona espaço extra para justificação, exceto na última palavra
if (align === 'justify' && i < line.length - 1) { if (align === 'justify' && i < line.length - 1) {o na última palavra
currentX += extraSpacing; currentX += extraSpacing;i < line.length - 1) {
} currentX += extraSpacing;
} }
} }
}
// Retorna a nova posição Y após desenhar a linha // Retorna a nova posição Y após desenhar a linha
return y - lineHeight; return y - lineHeight;ção Y após desenhar a linha
} return y - lineHeight;
} }
// Processe o texto com as novas funções melhoradas // Processe o texto com as novas funções melhoradas
const textoLimpo = stripUnsupportedTags( const textoLimpo = stripUnsupportedTags( melhoradas
this.processarTemplate(conteudo, escritorio) this.processarTemplate(conteudo, escritorio)
); );this.processarTemplate(conteudo, escritorio)
const texto = decodeHtmlEntities(textoLimpo);
const texto = decodeHtmlEntities(textoLimpo); const texto = decodeHtmlEntities(textoLimpo);
console.log('Texto processado para renderização:', texto.substring(0, 100) + '...'); console.log('Texto processado para renderização:', texto.substring(0, 100) + '...');
console.log('Texto processado para renderização:', texto.substring(0, 100) + '...');
// Corrigir o processamento de parágrafos para lidar melhor com formatações // Corrigir o processamento de parágrafos para lidar melhor com formatações
const paragrafos = texto.split(/\n{2,}/).map(p => { const paragrafos = texto.split(/\n{2,}/).map(p => {r melhor com formatações
let paragraph = p.trim(); let paragraph = p.trim();lit(/\n{2,}/).map(p => {
if (!paragraph) return ''; if (!paragraph) return '';
if (!paragraph) return '';
// Extrair informações de formatação // Extrair informações de formataçãoo a todos os parágrafos normais
let align = 'left'; // padrão let align = 'left'; // padrãorão é justificadomatação
const alignMatch = paragraph.match(/§§ALIGN=(center|right|justify)§§/); const alignMatch = paragraph.match(/§§ALIGN=(center|right|justify)§§/);
if (alignMatch) { if (alignMatch) { parece um título (começa com # ou tem menos de 50 caracteres e termina com :)= paragraph.match(/§§ALIGN=(center|right|justify)§§/);
align = alignMatch[1]; align = alignMatch[1];('#') ||
paragraph = paragraph.replace(/§§ALIGN=(center|right|justify)§§/, ''); paragraph = paragraph.replace(/§§ALIGN=(center|right|justify)§§/, '');
} } (paragraph.toUpperCase() === paragraph && paragraph.length < 100)) { paragraph = paragraph.replace(/§§ALIGN=(center|right|justify)§§/, '');
align = 'center'; // centraliza títulos}
let indent = 0; let indent = 0;aragraph.replace(/^#+\s*/, ''); // Remove marcadores de título #
const indentMatch = paragraph.match(/§§INDENT=(\d+)(pt|px|em)§§/); const indentMatch = paragraph.match(/§§INDENT=(\d+)(pt|px|em)§§/);
if (indentMatch) { if (indentMatch) {/§§INDENT=(\d+)(pt|px|em)§§/);
indent = parseInt(indentMatch[1]); indent = parseInt(indentMatch[1]);nhamento se estiverem presentes explicitamente
if (indentMatch[2] === 'em') indent *= 12; // Converter em para pontos aproximadamente if (indentMatch[2] === 'em') indent *= 12; // Converter em para pontos aproximadamente
paragraph = paragraph.replace(/§§INDENT=\d+(pt|px|em)§§/, ''); paragraph = paragraph.replace(/§§INDENT=\d+(pt|px|em)§§/, '');
} } align = alignMatch[1]; paragraph = paragraph.replace(/§§INDENT=\d+(pt|px|em)§§/, '');
paragraph = paragraph.replace(/§§ALIGN=(center|right|justify|left)§§/, '');}
return `§§ALIGN=${align}§§§§INDENT=${indent}§§${paragraph.trim()}`; return { text: paragraph.trim(), align, indent };
}).filter(Boolean); }).filter(Boolean);=${indent}§§${paragraph.trim()}`;
let indent = 0;}).filter(Boolean);
let currentPar = 0; let currentPar = 0; = paragraph.match(/§§INDENT=(\d+)(pt|px|em)?§§/);
let pageIndex = 0; let pageIndex = 0; {;
console.log(`Gerando PDF com ${paragrafos.length} parágrafos`); console.log(`Gerando PDF com ${paragrafos.length} parágrafos`);
if (indentMatch[2] === 'em') indent *= 12; // Converter em para pontos aproximadamenteconsole.log(`Gerando PDF com ${paragrafos.length} parágrafos`);
// Define um contador máximo para evitar loops infinitos // Define um contador máximo para evitar loops infinitos)?§§/, '');
const MAX_PAGES = 100; const MAX_PAGES = 100;loops infinitos
while (currentPar < paragrafos.length && pageIndex < MAX_PAGES) { while (currentPar < paragrafos.length && pageIndex < MAX_PAGES) {
console.log(`Criando página ${pageIndex + 1}, processando parágrafo ${currentPar + 1} de ${paragrafos.length}`); console.log(`Criando página ${pageIndex + 1}, processando parágrafo ${currentPar + 1} de ${paragrafos.length}`);
let page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]); let page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]);+ 1} de ${paragrafos.length}`);
align: align as 'left' | 'center' | 'right' | 'justify',let page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]);
// Adicionar timbrado à página // Adicionar timbrado à página
if (timbradoPages.length > 0) { if (timbradoPages.length > 0) {
console.log(`Adicionando timbrado na página ${pageIndex + 1}`); console.log(`Adicionando timbrado na página ${pageIndex + 1}`);
page.drawPage(timbradoPages[0], { page.drawPage(timbradoPages[0], {
x: 0, x: 0,Par = 0;awPage(timbradoPages[0], {
y: 0, y: 0,ex = 0;
width: PAGE_WIDTH, width: PAGE_WIDTH, com ${paragrafos.length} parágrafos`);
height: PAGE_HEIGHT height: PAGE_HEIGHT
}); });ne um contador máximo para evitar loops infinitoseight: PAGE_HEIGHT
} }st MAX_PAGES = 100; });
ile (currentPar < paragrafos.length && pageIndex < MAX_PAGES) {}
let y = PAGE_HEIGHT - margens.top; let y = PAGE_HEIGHT - margens.top;Index + 1}, processando parágrafo ${currentPar + 1} de ${paragrafos.length}`);
let processedAnyParagraph = false; // Flag para verificar se processamos algum parágrafo
let processedAnyParagraph = false; // Flag para verificar se processamos algum parágrafo let processedAnyParagraph = false; // Flag para verificar se processamos algum parágrafo
// Tenta processar parágrafos na página atual // Tenta processar parágrafos na página atual
while (currentPar < paragrafos.length) { while (currentPar < paragrafos.length) {
const paraText = paragrafos[currentPar]; const paraObj = paragrafos[currentPar];ina ${pageIndex + 1}`);
page.drawPage(timbradoPages[0], {const paraText = paragrafos[currentPar];
// Extrair propriedades do parágrafo
const text = paraObj.text;
const align = paraObj.align || 'justify'; // Por padrão, justifica o texto
const indent = paraObj.indent || 0;IGHT= paraText.match(/^§§ALIGN=(center|justify|right)§§/);
// Extract alignment let x = margens.left + indent;lign = alignMatch[1] as 'center' | 'justify' | 'left';
let align: 'left' | 'center' | 'justify' = 'left';
const alignMatch = paraText.match(/^§§ALIGN=(center|justify|right)§§/);
if (alignMatch) {
align = alignMatch[1] as 'center' | 'justify' | 'left';
}
// Extract indentation
let indent = 0;
const indentMatch = paraText.match(/§§INDENT=([0-9.]+)§§/);
if (indentMatch) {
indent = parseFloat(indentMatch[1]);
}
// Clean text
let text = paraText
.replace(/^§§ALIGN=(center|justify|right)§§/, '')
.replace(/§§INDENT=[0-9.]+§§/, '')
.trim();
let x = margens.left + indent;
let parWidth = MAX_WIDTH - indent; let parWidth = MAX_WIDTH - indent;
rgens.top;
// Se o texto for muito curto, avance para o próximo parágrafo // Se o texto for muito curto, avance para o próximo parágrafoaragraph = false; // Flag para verificar se processamos algum parágrafontation
if (text.length < 2) { if (text.length < 2) {
currentPar++; currentPar++;arágrafos na página atual= paraText.match(/§§INDENT=([0-9.]+)§§/);
processedAnyParagraph = true; processedAnyParagraph = true;
continue; continue;onst paraText = paragrafos[currentPar]; indent = parseFloat(indentMatch[1]);
} }}
ignment
console.log(`Tentando renderizar parágrafo ${currentPar + 1} em y=${y}`); console.log(`Tentando renderizar parágrafo ${currentPar + 1} em y=${y} com alinhamento ${align}`); 'center' | 'justify' = 'left';
let yAfter = renderInlineFormattedParagraph( let yAfter = renderInlineFormattedParagraph(|justify|right)§§/);
page, page,
text, text,alignMatch[1] as 'center' | 'justify' | 'left';(/§§INDENT=[0-9.]+§§/, '')
x, x,
y, y,
font, font,// Extract indentationlet parWidth = MAX_WIDTH - indent;
fontBold, fontBold,
fontItalic, fontItalic,raText.match(/§§INDENT=([0-9.]+)§§/);o curto, avance para o próximo parágrafo
fontBoldItalic, fontBoldItalic,) { < 2) {
FONT_SIZE, FONT_SIZE,ch[1]);
rgb(0, 0, 0), rgb(0, 0, 0),ph = true;
align, align,continue;
parWidth, parWidth,// Clean text}
false, false,
margens.top, margens.top,§/, ''){currentPar + 1} em y=${y}`);
margens.bottom, margens.bottom,ace(/§§INDENT=[0-9.]+§§/, '')ter = renderInlineFormattedParagraph(
PAGE_HEIGHT PAGE_HEIGHT();
); );x = margens.left + indent;xt,
parWidth = MAX_WIDTH - indent;
if (yAfter === null) { if (yAfter === null) {
// Se não conseguimos renderizar este parágrafo, vamos para a próxima página // Se não conseguimos renderizar este parágrafo, vamos para a próxima páginato for muito curto, avance para o próximo parágrafo
// Mas só se já tivermos processado algum parágrafo nesta página // Mas só se já tivermos processado algum parágrafo nesta páginath < 2) {
if (processedAnyParagraph) { if (processedAnyParagraph) {
console.log(`Página ${pageIndex + 1} está cheia, criando nova página`); console.log(`Página ${pageIndex + 1} está cheia, criando nova página`);nyParagraph = true;alic,
break; break;
} else { } else {
// Se não conseguimos nem processar um parágrafo, algo está errado // Se não conseguimos nem processar um parágrafo, algo está errado
// Vamos avançar para o próximo parágrafo para evitar um loop infinito // Vamos avançar para o próximo parágrafo para evitar um loop infinitolog(`Tentando renderizar parágrafo ${currentPar + 1} em y=${y}`);th,
console.log(`Não foi possível processar o parágrafo ${currentPar + 1}, avançando`); console.log(`Não foi possível processar o parágrafo ${currentPar + 1}, avançando`);enderInlineFormattedParagraph(
currentPar++; currentPar++;
processedAnyParagraph = true; processedAnyParagraph = true;
} }x,PAGE_HEIGHT
} else { } else { y,);
// Parágrafo foi renderizado com sucesso // Parágrafo foi renderizado com sucesso
y = yAfter - FONT_SIZE * 0.5; y = yAfter - FONT_SIZE * 0.5;
currentPar++; currentPar++;
processedAnyParagraph = true; processedAnyParagraph = true; parágrafo nesta página
console.log(`Parágrafo ${currentPar} processado com sucesso, nova posição y=${y}`); console.log(`Parágrafo ${currentPar} processado com sucesso, nova posição y=${y}`);
, 0),e.log(`Página ${pageIndex + 1} está cheia, criando nova página`);
// Verifica se ainda temos espaço para pelo menos uma linha // Verifica se ainda temos espaço para pelo menos uma linha
if (y < margens.bottom + FONT_SIZE * 2) { if (y < margens.bottom + FONT_SIZE * 2) {
console.log(`Espaço insuficiente para próximo parágrafo, criando nova página`); console.log(`Espaço insuficiente para próximo parágrafo, criando nova página`);
break; break;
}Não foi possível processar o parágrafo ${currentPar + 1}, avançando`);
} }
} }processedAnyParagraph = true;
}
pageIndex++; pageIndex++;
} }zar este parágrafo, vamos para a próxima páginacom sucesso
já tivermos processado algum parágrafo nesta páginaFONT_SIZE * 0.5;
if (pageIndex >= MAX_PAGES) { if (pageIndex >= MAX_PAGES) {
console.warn(`AVISO: Número máximo de páginas (${MAX_PAGES}) atingido. Geração de PDF interrompida.`); console.warn(`AVISO: Número máximo de páginas (${MAX_PAGES}) atingido. Geração de PDF interrompida.`);
} } break;console.log(`Parágrafo ${currentPar} processado com sucesso, nova posição y=${y}`);
console.log(`PDF gerado com ${pageIndex} páginas`); console.log(`PDF gerado com ${pageIndex} páginas`);parágrafo, algo está erradolo menos uma linha
const pdfBytes = await pdfDoc.save(); const pdfBytes = await pdfDoc.save();
return Buffer.from(pdfBytes); return Buffer.from(pdfBytes);e.log(`Não foi possível processar o parágrafo ${currentPar + 1}, avançando`);e.log(`Espaço insuficiente para próximo parágrafo, criando nova página`);
} catch (error) { } catch (error) { currentPar++; break;
console.error('Erro geral ao gerar PDF:', error); console.error('Erro geral ao gerar PDF:', error); processedAnyParagraph = true; }
throw error; throw error; } }
} } } else {}
} }rafo foi renderizado com sucesso
y = yAfter - FONT_SIZE * 0.5; pageIndex++;
private processarTemplate(template: string, dados: any): string { private processarTemplate(template: string, dados: any): string { currentPar++;}
return template.replace(/\{\{([^}]+)\}\}/g, (match, key) => { return template.replace(/\{\{([^}]+)\}\}/g, (match, key) => { true;
const keyParts = key.trim().split('.'); const keyParts = key.trim().split('.');
let value = dados; let value = dados; console.warn(`AVISO: Número máximo de páginas (${MAX_PAGES}) atingido. Geração de PDF interrompida.`);
for (const part of keyParts) { for (const part of keyParts) { // Verifica se ainda temos espaço para pelo menos uma linha }
if (value && typeof value === 'object' && part in value) { if (value && typeof value === 'object' && part in value) {
value = value[part]; value = value[part];nte para próximo parágrafo, criando nova página`);ex} páginas`);
} else { } else {
value = undefined; value = undefined;ytes);
break; break;
} }eral ao gerar PDF:', error);
} } throw error;
return value !== undefined ? value : match; return value !== undefined ? value : match; pageIndex++; }
}); }); } }
} }
} }
} } }); return value !== undefined ? value : match; } } break; value = undefined; } else { value = value[part]; if (value && typeof value === 'object' && part in value) { for (const part of keyParts) { let value = dados;