tenta fix pdfservice.js

This commit is contained in:
rodrigo committed 2025-04-22 10:21:45 -03:00
1 parent fefc6ce434
commit 92239d0a99
1 file changed
+248
+248
View File
@@ -348,6 +348,254 @@ export class PdfService {
.replace(/&#(\d+);/g, (m, code) => String.fromCharCode(code)); .replace(/&#(\d+);/g, (m, code) => String.fromCharCode(code));
} }
/**
* Renderiza um parágrafo com formatação inline (negrito, itálico, etc)
* Retorna a nova posição Y após renderizar o parágrafo, ou null se precisar de nova página
*/
function renderInlineFormattedParagraph(
page: any,
text: string,
x: number,
y: number,
font: any,
fontBold: any,
fontItalic: any,
fontBoldItalic: any,
fontSize: number,
color: any,
align: 'left' | 'center' | 'justify' = 'left',
maxWidth: number,
forceNewPage: boolean = false,
marginTop: number,
marginBottom: number,
pageHeight: number
): number | null {
if (forceNewPage || y < marginBottom + fontSize * 2) {
return null; // Indica necessidade de nova página
}
// Dividir o texto em palavras preservando as tags
const segments: { text: string; isBold: boolean; isItalic: boolean }[] = [];
let currentSegment = { text: '', isBold: false, isItalic: false };
// Regex para encontrar tags <b>, </b>, <i>, </i>, <strong>, </strong>, <em>, </em>
const regex = /<\/?(?:b|strong|i|em)>/gi;
let lastIndex = 0;
let match;
// Clone do texto para manipulação
let textToProcess = text;
// Processar tags de indentação e alinhamento
let indentPt = 0;
let textAlign = align;
const indentMatch = textToProcess.match(/^§§INDENT=([0-9.]+(?:pt|px|em))§§/);
if (indentMatch) {
const indentValue = indentMatch[1];
indentPt = parseInt(indentValue);
if (isNaN(indentPt)) indentPt = 20; // Padrão: 20pt
textToProcess = textToProcess.substring(indentMatch[0].length);
x += indentPt;
maxWidth -= indentPt;
}
const alignMatch = textToProcess.match(/^§§ALIGN=(center|justify|right)§§/);
if (alignMatch) {
textAlign = alignMatch[1] as 'center' | 'justify' | 'left';
textToProcess = textToProcess.substring(alignMatch[0].length);
}
while ((match = regex.exec(textToProcess)) !== null) {
// Adiciona texto entre as tags
if (match.index > lastIndex) {
currentSegment.text += textToProcess.substring(lastIndex, match.index);
}
// Processa a tag
const tag = match[0].toLowerCase();
if (tag === '<b>' || tag === '<strong>') {
// Finaliza o segmento atual se não vazio
if (currentSegment.text) {
segments.push({ ...currentSegment });
currentSegment.text = '';
}
currentSegment.isBold = true;
} else if (tag === '</b>' || tag === '</strong>') {
// Finaliza o segmento atual se não vazio
if (currentSegment.text) {
segments.push({ ...currentSegment });
currentSegment.text = '';
}
currentSegment.isBold = false;
} else if (tag === '<i>' || tag === '<em>') {
// Finaliza o segmento atual se não vazio
if (currentSegment.text) {
segments.push({ ...currentSegment });
currentSegment.text = '';
}
currentSegment.isItalic = true;
} else if (tag === '</i>' || tag === '</em>') {
// Finaliza o segmento atual se não vazio
if (currentSegment.text) {
segments.push({ ...currentSegment });
currentSegment.text = '';
}
currentSegment.isItalic = false;
}
lastIndex = match.index + match[0].length;
}
// Adiciona o texto restante
if (lastIndex < textToProcess.length) {
currentSegment.text += textToProcess.substring(lastIndex);
}
// Adiciona o último segmento se não estiver vazio
if (currentSegment.text) {
segments.push(currentSegment);
}
// Processa cada segmento e divide em linhas
const lines: { segments: { text: string; isBold: boolean; isItalic: boolean; width: number; font: any }[]; totalWidth: number }[] = [];
let currentLine: { segments: { text: string; isBold: boolean; isItalic: boolean; width: number; font: any }[]; totalWidth: number } = {
segments: [],
totalWidth: 0
};
for (const segment of segments) {
const words = segment.text.split(/\s+/);
for (let i = 0; i < words.length; i++) {
const word = words[i];
if (!word) continue;
// Adiciona espaço antes se não for a primeira palavra da linha
const wordWithSpace = currentLine.segments.length > 0 ? ' ' + word : word;
// Seleciona a fonte correta com base na formatação
let segmentFont;
if (segment.isBold && segment.isItalic) {
segmentFont = fontBoldItalic;
} else if (segment.isBold) {
segmentFont = fontBold;
} else if (segment.isItalic) {
segmentFont = fontItalic;
} else {
segmentFont = font;
}
const wordWidth = segmentFont.widthOfTextAtSize(wordWithSpace, fontSize);
// Se a palavra cabe na linha atual
if (currentLine.totalWidth + wordWidth <= maxWidth) {
currentLine.segments.push({
text: wordWithSpace,
isBold: segment.isBold,
isItalic: segment.isItalic,
width: wordWidth,
font: segmentFont
});
currentLine.totalWidth += wordWidth;
} else {
// Se a linha atual não está vazia, finalize-a
if (currentLine.segments.length > 0) {
lines.push(currentLine);
currentLine = { segments: [], totalWidth: 0 };
}
// Se a palavra for maior que a largura máxima, divida-a
if (segmentFont.widthOfTextAtSize(word, fontSize) > maxWidth) {
// Implementação simplificada: apenas adiciona a palavra muito longa
// em uma nova linha, mesmo que ultrapasse a largura máxima
currentLine.segments.push({
text: word,
isBold: segment.isBold,
isItalic: segment.isItalic,
width: segmentFont.widthOfTextAtSize(word, fontSize),
font: segmentFont
});
currentLine.totalWidth = segmentFont.widthOfTextAtSize(word, fontSize);
lines.push(currentLine);
currentLine = { segments: [], totalWidth: 0 };
} else {
// Caso contrário, adicione a palavra na nova linha
currentLine.segments.push({
text: word,
isBold: segment.isBold,
isItalic: segment.isItalic,
width: segmentFont.widthOfTextAtSize(word, fontSize),
font: segmentFont
});
currentLine.totalWidth = segmentFont.widthOfTextAtSize(word, fontSize);
}
}
}
}
// Adiciona a última linha se não estiver vazia
if (currentLine.segments.length > 0) {
lines.push(currentLine);
}
// Se não houver linhas após o processamento, retorne a posição Y atual
if (lines.length === 0) {
return y;
}
// Verifica se todas as linhas cabem na página atual
const totalHeight = lines.length * (fontSize * 1.2);
if (y - totalHeight < marginBottom) {
return null; // Precisa de uma nova página
}
// Renderiza as linhas
let currentY = y;
const lineHeight = fontSize * 1.2;
for (const line of lines) {
let currentX = x;
// Ajusta o X inicial com base no alinhamento
if (textAlign === 'center') {
currentX = x + (maxWidth - line.totalWidth) / 2;
} else if (textAlign === 'justify' && line !== lines[lines.length - 1] && line.segments.length > 1) {
// Para justificado, ajusta apenas linhas que não sejam a última e tenham mais de um segmento
const extraSpace = maxWidth - line.totalWidth;
const spaceBetweenSegments = extraSpace / (line.segments.length - 1);
for (const segment of line.segments) {
const font = segment.font;
page.drawText(segment.text, {
x: currentX,
y: currentY,
size: fontSize,
font: font,
color: color
});
currentX += segment.width + spaceBetweenSegments;
}
} else {
// Para alinhamento à esquerda ou a última linha de justificado
for (const segment of line.segments) {
const font = segment.font;
page.drawText(segment.text, {
x: currentX,
y: currentY,
size: fontSize,
font: font,
color: color
});
currentX += segment.width;
}
}
currentY -= lineHeight;
}
return currentY;
}
const texto = decodeHtmlEntities( const texto = decodeHtmlEntities(
stripUnsupportedTags( stripUnsupportedTags(
this.processarTemplate(conteudo, escritorio) this.processarTemplate(conteudo, escritorio)