tenta fix pdfservice.js

This commit is contained in:
rodrigo committed 2025-04-22 14:44:00 -03:00
1 parent 045124be2f
commit 6fdfc99e7d
1 file changed
+99 -35
+99 -35
View File
@@ -377,6 +377,12 @@ export class PdfService {
let currentLine: Array<{text: string, font: any, width: number}> = []; let currentLine: Array<{text: string, font: any, width: number}> = [];
let currentLineWidth = 0; let currentLineWidth = 0;
// Verifica se o texto parece ser um título (todo em maiúsculas ou curto)
const isTitle = text.toUpperCase() === text || text.length < 50;
// Se for título, centralizar independente do alinhamento fornecido
const effectiveAlign = isTitle ? 'center' : align;
// Processa texto com formatação - remover tags HTML e aplicar estilos // Processa texto com formatação - remover tags HTML e aplicar estilos
let currentFont = normalFont; let currentFont = normalFont;
let boldActive = false; let boldActive = false;
@@ -470,6 +476,17 @@ export class PdfService {
return null; // Não há espaço suficiente para renderizar return null; // Não há espaço suficiente para renderizar
} }
// Aumentar ligeiramente o espaçamento de linhas para títulos
const effectiveLineHeight = isTitle ? lineHeight * 1.3 : lineHeight;
// Aumentar o tamanho da fonte para títulos
const effectiveFontSize = isTitle ? fontSize * 1.2 : fontSize;
// Se for um título, adicionar um espaço extra antes
if (isTitle && y < startY) {
y -= lineHeight * 0.5;
}
// Agora, renderizar as palavras em linhas // Agora, renderizar as palavras em linhas
for (let i = 0; i < words.length; i++) { for (let i = 0; i < words.length; i++) {
const word = words[i]; const word = words[i];
@@ -481,18 +498,23 @@ export class PdfService {
currentLineWidth += word.width; currentLineWidth += word.width;
} else { } else {
// Renderizar a linha atual // Renderizar a linha atual
if (y - lineHeight < marginBottom) { if (y - effectiveLineHeight < marginBottom) {
return null; // Não há mais espaço na página return null; // Não há mais espaço na página
} }
// Ajustar X baseado no alinhamento // Ajustar X baseado no alinhamento
let lineX = x; let lineX = x;
const spacingFactor = align === 'justify' && currentLine.length > 1 ?
// Para justificado, calcular espaçamentos extras entre palavras
// Mas não justificar a última linha de um parágrafo ou títulos
const isLastLine = i === words.length - 1;
const shouldJustify = effectiveAlign === 'justify' && !isLastLine && !isTitle && currentLine.length > 1;
const extraSpacing = shouldJustify ?
(maxWidth - currentLineWidth) / (currentLine.length - 1) : 0; (maxWidth - currentLineWidth) / (currentLine.length - 1) : 0;
if (align === 'center') { if (effectiveAlign === 'center') {
lineX = x + (maxWidth - currentLineWidth) / 2; lineX = x + (maxWidth - currentLineWidth) / 2;
} else if (align === 'right') { } else if (effectiveAlign === 'right') {
lineX = x + maxWidth - currentLineWidth; lineX = x + maxWidth - currentLineWidth;
} }
@@ -500,11 +522,13 @@ export class PdfService {
let posX = lineX; let posX = lineX;
for (let j = 0; j < currentLine.length; j++) { for (let j = 0; j < currentLine.length; j++) {
const item = currentLine[j]; const item = currentLine[j];
const itemFontSize = isTitle ? effectiveFontSize : fontSize;
page.drawText(item.text, { page.drawText(item.text, {
x: posX, x: posX,
y: y, y: y,
font: item.font, font: item.font,
size: fontSize, size: itemFontSize,
color color
}); });
@@ -512,18 +536,18 @@ export class PdfService {
posX += item.width; posX += item.width;
// Adicionar espaço extra se for justificado (exceto no final da linha) // Adicionar espaço extra se for justificado (exceto no final da linha)
if (align === 'justify' && j < currentLine.length - 1) { if (shouldJustify && j < currentLine.length - 1) {
posX += spacingFactor; posX += extraSpacing;
} }
} }
// Avançar para a próxima linha // Avançar para a próxima linha
y -= lineHeight; y -= effectiveLineHeight;
currentLine = [word]; currentLine = [word];
currentLineWidth = word.width; currentLineWidth = word.width;
// Verificar se ainda temos espaço // Verificar se ainda temos espaço
if (y - lineHeight < marginBottom) { if (y - effectiveLineHeight < marginBottom) {
return null; return null;
} }
} }
@@ -531,41 +555,37 @@ export class PdfService {
// Renderizar a última linha, se houver // Renderizar a última linha, se houver
if (currentLine.length > 0) { if (currentLine.length > 0) {
// Ajustar X baseado no alinhamento (última linha nunca é justificada) // Ajustar X baseado no alinhamento (última linha nunca é justificada, exceto se for curta)
let lineX = x; let lineX = x;
if (align === 'center') {
// Se for título ou alinhado ao centro/direita, ajustar posição
if (effectiveAlign === 'center') {
lineX = x + (maxWidth - currentLineWidth) / 2; lineX = x + (maxWidth - currentLineWidth) / 2;
} else if (align === 'right') { } else if (effectiveAlign === 'right') {
lineX = x + maxWidth - currentLineWidth; lineX = x + maxWidth - currentLineWidth;
} }
// Desenhar a linha // Desenhar a linha
let posX = lineX; let posX = lineX;
for (const item of currentLine) { for (const item of currentLine) {
const itemFontSize = isTitle ? effectiveFontSize : fontSize;
page.drawText(item.text, { page.drawText(item.text, {
x: posX, x: posX,
y: y, y: y,
font: item.font, font: item.font,
size: fontSize, size: itemFontSize,
color color
}); });
posX += item.width; posX += item.width;
} }
y -= lineHeight; y -= effectiveLineHeight;
} }
// Se estiver em modo debug, desenhar uma caixa ao redor do texto // Se for título, adicionar um espaço extra depois
if (debug) { if (isTitle) {
page.drawRectangle({ y -= lineHeight * 0.5;
x,
y: y + lineHeight,
width: maxWidth,
height: startY - y,
borderColor: rgb(0.8, 0.8, 0.8),
borderWidth: 0.5,
opacity: 0.1
});
} }
return y; return y;
@@ -581,22 +601,49 @@ export class PdfService {
const paragrafos = texto.split(/\n{2,}/).map(p => { const paragrafos = texto.split(/\n{2,}/).map(p => {
let paragraph = p.trim(); let paragraph = p.trim();
if (!paragraph) return ''; if (!paragraph) return '';
// Extrair informações de formatação // Extrair informações de formatação
let align = 'left'; // padrão é justificado let align = 'justify'; // Padrão agora é justificado para texto normal
const alignMatch = paragraph.match(/§§ALIGN=(center|right|justify)§§/);
// Verifica se o texto parece ser um título por diversos critérios:
// 1. Começa com '#' (estilo markdown)
// 2. Todo em maiúsculas e relativamente curto
// 3. Começa com números seguidos de ponto (como "1. Título")
// 4. Termina com dois pontos e é curto (como "Considerando:")
const isTitle =
paragraph.startsWith('#') ||
(paragraph.toUpperCase() === paragraph && paragraph.length < 100) ||
/^\d+\.\s+\S+/.test(paragraph) ||
(paragraph.endsWith(':') && paragraph.length < 70);
// Se for um título, centraliza
if (isTitle) {
align = 'center';
paragraph = paragraph.replace(/^#+\s*/, ''); // Remove marcadores de título estilo markdown
}
// Processa alinhamentos explícitos (têm precedência)
const alignMatch = paragraph.match(/§§ALIGN=(center|right|justify|left)§§/);
if (alignMatch) { if (alignMatch) {
align = alignMatch[1]; align = alignMatch[1];
paragraph = paragraph.replace(/§§ALIGN=(center|right|justify)§§/, ''); paragraph = paragraph.replace(/§§ALIGN=(center|right|justify|left)§§/, '');
} }
let indent = 0; let indent = 0;
const indentMatch = paragraph.match(/§§INDENT=(\d+)(pt|px|em)§§/); const indentMatch = paragraph.match(/§§INDENT=(\d+)(pt|px|em)?§§/);
if (indentMatch) { if (indentMatch) {
indent = parseInt(indentMatch[1]); indent = parseInt(indentMatch[1]);
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)?§§/, '');
} }
return { text: paragraph.trim(), align, indent };
}).filter(Boolean); return {
text: paragraph.trim(),
align: align as 'left' | 'center' | 'right' | 'justify',
indent,
isTitle
};
}).filter(p => p.text);
let currentPar = 0; let currentPar = 0;
let pageIndex = 0; let pageIndex = 0;
console.log(`Gerando PDF com ${paragrafos.length} parágrafos`); console.log(`Gerando PDF com ${paragrafos.length} parágrafos`);
@@ -621,18 +668,35 @@ export class PdfService {
while (currentPar < paragrafos.length) { while (currentPar < paragrafos.length) {
const paraObj = paragrafos[currentPar]; const paraObj = paragrafos[currentPar];
const text = paraObj.text; const text = paraObj.text;
const align = paraObj.align || 'justify'; // Por padrão, justifica o texto const align = paraObj.align;
const indent = paraObj.indent || 0; const indent = paraObj.indent || 0;
const isTitle = paraObj.isTitle || false;
let x = margens.left + indent; let x = margens.left + indent;
let parWidth = MAX_WIDTH - indent; let parWidth = MAX_WIDTH - indent;
// 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ágrafo
if (text.length < 2) { if (text.length < 2) {
currentPar++; currentPar++;
processedAnyParagraph = true; processedAnyParagraph = true;
continue; continue;
} }
console.log(`Tentando renderizar parágrafo ${currentPar + 1} em y=${y} com alinhamento ${align}`);
// Ajuste o tamanho da fonte para títulos
const fontSizeToUse = isTitle ? FONT_SIZE * 1.2 : FONT_SIZE;
// Adicionar espaço extra antes de títulos (exceto para o primeiro parágrafo da página)
if (isTitle && processedAnyParagraph) {
y -= FONT_SIZE * 0.8;
// Se não caber na página depois do espaço, vá para a próxima página
if (y < margens.bottom + fontSizeToUse * 2) {
break;
}
}
console.log(`Tentando renderizar parágrafo ${currentPar + 1} em y=${y} com alinhamento ${align}${isTitle ? ' (título)' : ''}`);
let yAfter = renderInlineFormattedParagraph( let yAfter = renderInlineFormattedParagraph(
page, page,
text, text,
@@ -642,7 +706,7 @@ export class PdfService {
fontBold, fontBold,
fontItalic, fontItalic,
fontBoldItalic, fontBoldItalic,
FONT_SIZE, fontSizeToUse,
rgb(0, 0, 0), rgb(0, 0, 0),
align, align,
parWidth, parWidth,