diff --git a/src/server/services/PdfService.ts b/src/server/services/PdfService.ts
index a90112f..f73d36c 100644
--- a/src/server/services/PdfService.ts
+++ b/src/server/services/PdfService.ts
@@ -348,6 +348,254 @@ export class PdfService {
.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 , , , , , , ,
+ 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 === '' || tag === '') {
+ // Finaliza o segmento atual se não vazio
+ if (currentSegment.text) {
+ segments.push({ ...currentSegment });
+ currentSegment.text = '';
+ }
+ currentSegment.isBold = true;
+ } else if (tag === '' || tag === '') {
+ // Finaliza o segmento atual se não vazio
+ if (currentSegment.text) {
+ segments.push({ ...currentSegment });
+ currentSegment.text = '';
+ }
+ currentSegment.isBold = false;
+ } else if (tag === '' || tag === '') {
+ // Finaliza o segmento atual se não vazio
+ if (currentSegment.text) {
+ segments.push({ ...currentSegment });
+ currentSegment.text = '';
+ }
+ currentSegment.isItalic = true;
+ } else if (tag === '' || tag === '') {
+ // 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(
stripUnsupportedTags(
this.processarTemplate(conteudo, escritorio)