tenta fix pdfservice.js

This commit is contained in:
rodrigo committed 2025-04-22 11:33:45 -03:00
1 parent bbff8eb317
commit 20b2f0e3c9
1 file changed
+253
+253
View File
@@ -355,6 +355,259 @@ export class PdfService {
.replace(/>/g, '>'); .replace(/>/g, '>');
} }
/**
* Renderiza um parágrafo com formatação inline (negrito, itálico)
* @param page - Página do PDF
* @param text - Texto a ser renderizado
* @param x - Posição X inicial
* @param y - Posição Y inicial
* @param regularFont - Fonte normal
* @param boldFont - Fonte negrito
* @param italicFont - Fonte itálica
* @param boldItalicFont - Fonte negrito+itálica
* @param fontSize - Tamanho da fonte
* @param color - Cor do texto
* @param align - Alinhamento do texto
* @param maxWidth - Largura máxima
* @param debug - Modo debug
* @param marginTop - Margem superior
* @param marginBottom - Margem inferior
* @param pageHeight - Altura da página
* @returns Nova posição Y ou null se não couber na página
*/
function renderInlineFormattedParagraph(
page: any,
text: string,
x: number,
y: number,
regularFont: any,
boldFont: any,
italicFont: any,
boldItalicFont: any,
fontSize: number,
color: any,
align: 'left' | 'center' | 'right' | 'justify' = 'left',
maxWidth: number,
debug: boolean = false,
marginTop: number,
marginBottom: number,
pageHeight: number
): number | null {
if (y < marginBottom || y > pageHeight - marginTop) {
return null; // Fora dos limites da página
}
// Considera a linha inteira
const lineHeight = fontSize * 1.2;
// Separa o texto em segmentos de formatação
const segments: { text: string; isBold: boolean; isItalic: boolean }[] = [];
let remaining = text;
// Padrões para encontrar tags de formatação inline
const boldStartPattern = /<b>/i;
const boldEndPattern = /<\/b>/i;
const italicStartPattern = /<i>/i;
const italicEndPattern = /<\/i>/i;
let boldActive = false;
let italicActive = false;
// Extrai segmentos formatados do texto
while (remaining.length > 0) {
const boldStartMatch = remaining.search(boldStartPattern);
const boldEndMatch = remaining.search(boldEndPattern);
const italicStartMatch = remaining.search(italicStartPattern);
const italicEndMatch = remaining.search(italicEndPattern);
// Encontra a próxima tag
let nextTagPos = -1;
let nextTagType = '';
if (boldStartMatch !== -1 && (nextTagPos === -1 || boldStartMatch < nextTagPos)) {
nextTagPos = boldStartMatch;
nextTagType = 'boldStart';
}
if (boldEndMatch !== -1 && (nextTagPos === -1 || boldEndMatch < nextTagPos)) {
nextTagPos = boldEndMatch;
nextTagType = 'boldEnd';
}
if (italicStartMatch !== -1 && (nextTagPos === -1 || italicStartMatch < nextTagPos)) {
nextTagPos = italicStartMatch;
nextTagType = 'italicStart';
}
if (italicEndMatch !== -1 && (nextTagPos === -1 || italicEndMatch < nextTagPos)) {
nextTagPos = italicEndMatch;
nextTagType = 'italicEnd';
}
if (nextTagPos === -1) {
// Não há mais tags, adicione o texto restante como um único segmento
segments.push({
text: remaining,
isBold: boldActive,
isItalic: italicActive,
});
break;
} else {
// Adiciona texto antes da tag
if (nextTagPos > 0) {
segments.push({
text: remaining.substring(0, nextTagPos),
isBold: boldActive,
isItalic: italicActive,
});
}
// Atualiza o estado de formatação com base na tag
if (nextTagType === 'boldStart') {
boldActive = true;
remaining = remaining.substring(nextTagPos + 3); // <b> tem 3 caracteres
} else if (nextTagType === 'boldEnd') {
boldActive = false;
remaining = remaining.substring(nextTagPos + 4); // </b> tem 4 caracteres
} else if (nextTagType === 'italicStart') {
italicActive = true;
remaining = remaining.substring(nextTagPos + 3); // <i> tem 3 caracteres
} else if (nextTagType === 'italicEnd') {
italicActive = false;
remaining = remaining.substring(nextTagPos + 4); // </i> tem 4 caracteres
}
}
}
// Função para quebrar um texto em palavras
const getWords = (text: string): string[] => {
return text.split(/(\s+)/).filter(word => word.length > 0);
};
// Calcular largura de texto
const getTextWidth = (text: string, font: any): number => {
return font.widthOfTextAtSize(text, fontSize);
};
// Organiza palavras em linhas, respeitando a formatação
const lines: {
segments: { text: string; isBold: boolean; isItalic: boolean }[];
width: number;
}[] = [];
let currentLine: {
segments: { text: string; isBold: boolean; isItalic: boolean }[];
width: number;
} = { segments: [], width: 0 };
for (const segment of segments) {
const words = getWords(segment.text);
for (const word of words) {
// Para espaços, apenas adicione-os diretamente
if (/^\s+$/.test(word)) {
if (currentLine.segments.length > 0) {
const lastSegment = currentLine.segments[currentLine.segments.length - 1];
// Apenas adicione espaço se não for o primeiro segmento da linha
lastSegment.text += word;
const font = getFont(lastSegment.isBold, lastSegment.isItalic);
currentLine.width += getTextWidth(word, font);
}
continue;
}
const font = getFont(segment.isBold, segment.isItalic);
const wordWidth = getTextWidth(word, font);
// Verifica se a palavra cabe na linha atual
if (currentLine.segments.length === 0 || currentLine.width + wordWidth <= maxWidth) {
// Tenta adicionar à última parte do mesmo tipo de formatação
if (
currentLine.segments.length > 0 &&
currentLine.segments[currentLine.segments.length - 1].isBold === segment.isBold &&
currentLine.segments[currentLine.segments.length - 1].isItalic === segment.isItalic
) {
currentLine.segments[currentLine.segments.length - 1].text += word;
} else {
// Adiciona novo segmento
currentLine.segments.push({
text: word,
isBold: segment.isBold,
isItalic: segment.isItalic,
});
}
currentLine.width += wordWidth;
} else {
// A palavra não cabe, iniciar nova linha
lines.push(currentLine);
currentLine = {
segments: [
{
text: word,
isBold: segment.isBold,
isItalic: segment.isItalic,
},
],
width: wordWidth,
};
}
}
}
// Adiciona a última linha se não estiver vazia
if (currentLine.segments.length > 0) {
lines.push(currentLine);
}
// Função auxiliar para obter a fonte correta
function getFont(isBold: boolean, isItalic: boolean): any {
if (isBold && isItalic) return boldItalicFont;
if (isBold) return boldFont;
if (isItalic) return italicFont;
return regularFont;
}
// Renderiza as linhas
let currentY = y;
for (const line of lines) {
// Se não couber mais na página, retorna null
if (currentY - lineHeight < marginBottom) {
return null;
}
let lineX = x;
// Ajusta a posição X com base no alinhamento
if (align === 'center') {
lineX = x + (maxWidth - line.width) / 2;
} else if (align === 'right') {
lineX = x + maxWidth - line.width;
}
// Renderiza cada segmento da linha
for (const segment of line.segments) {
const font = getFont(segment.isBold, segment.isItalic);
page.drawText(segment.text, {
x: lineX,
y: currentY,
font,
size: fontSize,
color,
});
lineX += getTextWidth(segment.text, font);
}
// Move para a próxima linha
currentY -= lineHeight;
}
return currentY;
}
// Processe o texto com as novas funções melhoradas // Processe o texto com as novas funções melhoradas
const textoLimpo = stripUnsupportedTags( const textoLimpo = stripUnsupportedTags(
this.processarTemplate(conteudo, escritorio) this.processarTemplate(conteudo, escritorio)