tenta fix pdfservice.js
This commit is contained in:
1 parent
20b2f0e3c9
commit
19f0187227
1 file changed
+10
-272
@@ -284,35 +284,28 @@ export class PdfService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Melhorar a função que remove tags HTML
|
|
||||||
function stripUnsupportedTags(html: string): string {
|
function stripUnsupportedTags(html: string): string {
|
||||||
// Primeiro, substituir entidades e tags comuns
|
// Primeiro, substituir entidades e tags comuns
|
||||||
const cleanHtml = html
|
const cleanHtml = html
|
||||||
// Preserva quebras de linha HTML como marcadores especiais
|
// Preserva quebras de linha HTML como marcadores especiais
|
||||||
.replace(/<br\s*\/?>|<\/p><p>|<\/div><div>/gi, '\n')
|
.replace(/<br\s*\/?>|<\/p><p>|<\/div><div>/gi, '\n')
|
||||||
// Substitui parágrafos, divs e blockquote por marcadores de bloco + quebra de linha
|
// Substitui parágrafos, divs e blockquote por marcadores de bloco + quebra de linha
|
||||||
|
.replace(/<p[^>]*style="[^"]*text-align\s*:\s*(center|right|justify)[^"]*"[^>]*>/gi,
|
||||||
|
(match, align) => `<p style="text-align:${align}">`)
|
||||||
|
.replace(/<div[^>]*style="[^"]*text-align\s*:\s*(center|right|justify)[^"]*"[^>]*>/gi,
|
||||||
|
(match, align) => `<div style="text-align:${align}">`)
|
||||||
.replace(/<p[^>]*>|<div[^>]*>|<blockquote[^>]*>/gi, '')
|
.replace(/<p[^>]*>|<div[^>]*>|<blockquote[^>]*>/gi, '')
|
||||||
.replace(/<\/p>|<\/div>|<\/blockquote>/gi, '\n\n')
|
.replace(/<\/p>|<\/div>|<\/blockquote>/gi, '\n\n')
|
||||||
// Marca formatação de texto
|
// Marca formatação de texto
|
||||||
.replace(/<b>|<strong>/gi, '§§BOLD_START§§')
|
.replace(/<b>|<strong>/gi, '<b>')
|
||||||
.replace(/<\/b>|<\/strong>/gi, '§§BOLD_END§§')
|
.replace(/<\/b>|<\/strong>/gi, '</b>')
|
||||||
.replace(/<i>|<em>/gi, '§§ITALIC_START§§')
|
.replace(/<i>|<em>/gi, '<i>')
|
||||||
.replace(/<\/i>|<\/em>/gi, '§§ITALIC_END§§')
|
.replace(/<\/i>|<\/em>/gi, '</i>')
|
||||||
// Armazena alinhamento e indentação
|
|
||||||
.replace(/style="[^"]*text-align\s*:\s*(center|right|justify)[^"]*"/gi, (_, align) => `§§ALIGN=${align}§§`)
|
|
||||||
.replace(/style="[^"]*text-indent\s*:\s*(\d+(?:pt|px|em)[^"]*)"/gi, (_, indent) => `§§INDENT=${indent}§§`)
|
|
||||||
// Remove todas as outras tags HTML
|
|
||||||
.replace(/<[^>]+>/g, '')
|
|
||||||
// Restaura formatação de texto
|
|
||||||
.replace(/§§BOLD_START§§/g, '<b>')
|
|
||||||
.replace(/§§BOLD_END§§/g, '</b>')
|
|
||||||
.replace(/§§ITALIC_START§§/g, '<i>')
|
|
||||||
.replace(/§§ITALIC_END§§/g, '</i>');
|
|
||||||
|
|
||||||
return cleanHtml
|
|
||||||
// Normaliza espaços extras e quebras de linha
|
// Normaliza espaços extras e quebras de linha
|
||||||
.replace(/\n{3,}/g, '\n\n')
|
.replace(/\n{3,}/g, '\n\n')
|
||||||
.replace(/[ \t]+/g, ' ');
|
.replace(/[ \t]+/g, ' ');
|
||||||
|
|
||||||
|
return cleanHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Função para decodificar entidades HTML comuns
|
// Função para decodificar entidades HTML comuns
|
||||||
@@ -355,264 +348,10 @@ 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)
|
||||||
);
|
);
|
||||||
|
|
||||||
const texto = decodeHtmlEntities(textoLimpo);
|
const texto = decodeHtmlEntities(textoLimpo);
|
||||||
|
|
||||||
console.log('Texto processado para renderização:', texto.substring(0, 100) + '...');
|
console.log('Texto processado para renderização:', texto.substring(0, 100) + '...');
|
||||||
@@ -688,7 +427,6 @@ export class PdfService {
|
|||||||
.replace(/^§§ALIGN=(center|justify|right)§§/, '')
|
.replace(/^§§ALIGN=(center|justify|right)§§/, '')
|
||||||
.replace(/§§INDENT=[0-9.]+§§/, '')
|
.replace(/§§INDENT=[0-9.]+§§/, '')
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
let x = margens.left + indent;
|
let x = margens.left + indent;
|
||||||
let parWidth = MAX_WIDTH - indent;
|
let parWidth = MAX_WIDTH - indent;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user