tenta fix pdfservice.js
This commit is contained in:
1 parent
bbff8eb317
commit
20b2f0e3c9
1 file changed
+253
@@ -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)
|
||||||
|
|||||||
Reference in new issue
Block a user