From 045124be2f7c9b048900869609a3acaf3a8e7185 Mon Sep 17 00:00:00 2001 From: Rodrigo Martins Date: Tue, 22 Apr 2025 14:25:13 -0300 Subject: [PATCH] tenta fix pdfservice.js --- src/server/services/PdfService.ts | 179 +++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/src/server/services/PdfService.ts b/src/server/services/PdfService.ts index 7ef14a8..22c3169 100644 --- a/src/server/services/PdfService.ts +++ b/src/server/services/PdfService.ts @@ -376,7 +376,6 @@ export class PdfService { let words: Array<{text: string, font: any, width: number}> = []; let currentLine: Array<{text: string, font: any, width: number}> = []; let currentLineWidth = 0; - let textParts = []; // Processa texto com formatação - remover tags HTML e aplicar estilos let currentFont = normalFont; @@ -392,6 +391,184 @@ export class PdfService { .replace(/<(?!\/?(?:b|i|strong|em)(?!\w)[^>]*>)[^>]*>/gi, ''); // Manter apenas b, i, strong, em // Primeiro, extrair partes do texto com suas formatações + let remainingText = text; + let textParts: Array<{text: string, bold: boolean, italic: boolean}> = []; + + while (remainingText.length > 0) { + const boldStartMatch = remainingText.match(/|/i); + const italicStartMatch = remainingText.match(/|/i); + const boldEndMatch = remainingText.match(/<\/b>|<\/strong>/i); + const italicEndMatch = remainingText.match(/<\/i>|<\/em>/i); + + // Encontrar a próxima tag + const matches = [boldStartMatch, italicStartMatch, boldEndMatch, italicEndMatch] + .filter(m => m !== null); + + if (matches.length === 0) { + // Não há mais tags, adicionar o texto restante + if (remainingText.length > 0) { + textParts.push({ + text: remainingText, + bold: boldActive, + italic: italicActive + }); + } + break; + } + + // Ordenar matches por índice + matches.sort((a, b) => a!.index! - b!.index!); + const nextMatch = matches[0]!; + const matchIndex = nextMatch.index!; + + // Texto antes da próxima tag + if (matchIndex > 0) { + textParts.push({ + text: remainingText.substring(0, matchIndex), + bold: boldActive, + italic: italicActive + }); + } + + // Atualizar estado de formatação + if (nextMatch[0].match(/|/i)) boldActive = true; + else if (nextMatch[0].match(/<\/b>|<\/strong>/i)) boldActive = false; + else if (nextMatch[0].match(/|/i)) italicActive = true; + else if (nextMatch[0].match(/<\/i>|<\/em>/i)) italicActive = false; + + // Remover o texto processado e a tag + remainingText = remainingText.substring(matchIndex + nextMatch[0].length); + } + + // Processar as partes extraídas + for (const part of textParts) { + // Determinar a fonte baseado na formatação + let font = normalFont; + if (part.bold && part.italic) font = boldItalicFont; + else if (part.bold) font = boldFont; + else if (part.italic) font = italicFont; + + // Dividir em palavras + const partWords = part.text.split(/\s+/); + + for (const wordText of partWords) { + if (!wordText) continue; + + const width = font.widthOfTextAtSize(wordText, fontSize); + words.push({ text: wordText, font, width }); + words.push({ text: ' ', font, width: font.widthOfTextAtSize(' ', fontSize) }); + } + + // Remove o último espaço adicionado + if (words.length > 0) { + words.pop(); + } + } + + // Verificar se há espaço suficiente na página para pelo menos uma linha + if (y - lineHeight < marginBottom) { + return null; // Não há espaço suficiente para renderizar + } + + // Agora, renderizar as palavras em linhas + for (let i = 0; i < words.length; i++) { + const word = words[i]; + + // Verificar se a palavra cabe na linha atual + if (currentLineWidth + word.width <= maxWidth || currentLine.length === 0) { + // Adicionar palavra à linha atual + currentLine.push(word); + currentLineWidth += word.width; + } else { + // Renderizar a linha atual + if (y - lineHeight < marginBottom) { + return null; // Não há mais espaço na página + } + + // Ajustar X baseado no alinhamento + let lineX = x; + const spacingFactor = align === 'justify' && currentLine.length > 1 ? + (maxWidth - currentLineWidth) / (currentLine.length - 1) : 0; + + if (align === 'center') { + lineX = x + (maxWidth - currentLineWidth) / 2; + } else if (align === 'right') { + lineX = x + maxWidth - currentLineWidth; + } + + // Desenhar a linha + let posX = lineX; + for (let j = 0; j < currentLine.length; j++) { + const item = currentLine[j]; + page.drawText(item.text, { + x: posX, + y: y, + font: item.font, + size: fontSize, + color + }); + + // Avançar posição X + posX += item.width; + + // Adicionar espaço extra se for justificado (exceto no final da linha) + if (align === 'justify' && j < currentLine.length - 1) { + posX += spacingFactor; + } + } + + // Avançar para a próxima linha + y -= lineHeight; + currentLine = [word]; + currentLineWidth = word.width; + + // Verificar se ainda temos espaço + if (y - lineHeight < marginBottom) { + return null; + } + } + } + + // Renderizar a última linha, se houver + if (currentLine.length > 0) { + // Ajustar X baseado no alinhamento (última linha nunca é justificada) + let lineX = x; + if (align === 'center') { + lineX = x + (maxWidth - currentLineWidth) / 2; + } else if (align === 'right') { + lineX = x + maxWidth - currentLineWidth; + } + + // Desenhar a linha + let posX = lineX; + for (const item of currentLine) { + page.drawText(item.text, { + x: posX, + y: y, + font: item.font, + size: fontSize, + color + }); + posX += item.width; + } + + y -= lineHeight; + } + + // Se estiver em modo debug, desenhar uma caixa ao redor do texto + if (debug) { + page.drawRectangle({ + 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; } // Processe o texto com as novas funções melhoradas