tenta fix pdfservice.js

This commit is contained in:
rodrigo committed 2025-04-21 22:08:29 -03:00
1 parent 287ed34abf
commit eeeae75919
1 file changed
+70 -23
+70 -23
View File
@@ -329,38 +329,56 @@ export class PdfService {
.replace(/<!--[\s\S]*?-->/g, ''); .replace(/<!--[\s\S]*?-->/g, '');
} }
// Função para renderizar texto com formatação inline (negrito, itálico, ambos) e controle de quebra de linha // Função para renderizar texto com formatação inline (negrito, itálico, sublinhado) e controle de quebra de linha e recuo
function renderInlineFormattedParagraph(page, text: string, x: number, y: number, font, fontBold, fontItalic, fontBoldItalic, fontSize: number, color, align: string, maxWidth: number) { function renderInlineFormattedParagraph(
// Divide o texto em tokens de formatação (<b>, <i>, etc) page,
let regex = /(<\/?(b|strong|i|em)>)/gi; text: string,
let tokens: { text: string, bold: boolean, italic: boolean }[] = []; x: number,
y: number,
font,
fontBold,
fontItalic,
fontBoldItalic,
fontSize: number,
color,
align: string,
maxWidth: number,
indent: boolean
) {
// Divide o texto em tokens de formatação (<b>, <i>, <u>, etc)
let regex = /(<\/?(b|strong|i|em|u)>)/gi;
let tokens: { text: string, bold: boolean, italic: boolean, underline: boolean }[] = [];
let lastIndex = 0; let lastIndex = 0;
let match; let match;
let currentStyle = { bold: false, italic: false }; let currentStyle = { bold: false, italic: false, underline: false };
while ((match = regex.exec(text)) !== null) { while ((match = regex.exec(text)) !== null) {
if (match.index > lastIndex) { if (match.index > lastIndex) {
tokens.push({ tokens.push({
text: text.substring(lastIndex, match.index), text: text.substring(lastIndex, match.index),
bold: currentStyle.bold, bold: currentStyle.bold,
italic: currentStyle.italic italic: currentStyle.italic,
underline: currentStyle.underline
}); });
} }
if (/^<(b|strong)>$/i.test(match[0])) currentStyle.bold = true; if (/^<(b|strong)>$/i.test(match[0])) currentStyle.bold = true;
if (/^<\/(b|strong)>$/i.test(match[0])) currentStyle.bold = false; if (/^<\/(b|strong)>$/i.test(match[0])) currentStyle.bold = false;
if (/^<(i|em)>$/i.test(match[0])) currentStyle.italic = true; if (/^<(i|em)>$/i.test(match[0])) currentStyle.italic = true;
if (/^<\/(i|em)>$/i.test(match[0])) currentStyle.italic = false; if (/^<\/(i|em)>$/i.test(match[0])) currentStyle.italic = false;
if (/^<u>$/i.test(match[0])) currentStyle.underline = true;
if (/^<\/u>$/i.test(match[0])) currentStyle.underline = false;
lastIndex = regex.lastIndex; lastIndex = regex.lastIndex;
} }
if (lastIndex < text.length) { if (lastIndex < text.length) {
tokens.push({ tokens.push({
text: text.substring(lastIndex), text: text.substring(lastIndex),
bold: currentStyle.bold, bold: currentStyle.bold,
italic: currentStyle.italic italic: currentStyle.italic,
underline: currentStyle.underline
}); });
} }
// Junta todos os tokens em uma lista de palavras com formatação // Junta todos os tokens em uma lista de palavras com formatação
let words: { text: string, font: any }[] = []; let words: { text: string, font: any, underline: boolean }[] = [];
for (const token of tokens) { for (const token of tokens) {
let usedFont = font; let usedFont = font;
if (token.bold && token.italic) usedFont = fontBoldItalic; if (token.bold && token.italic) usedFont = fontBoldItalic;
@@ -370,19 +388,24 @@ export class PdfService {
const split = token.text.split(/(\s+)/); const split = token.text.split(/(\s+)/);
for (const part of split) { for (const part of split) {
if (part.length > 0) { if (part.length > 0) {
words.push({ text: part, font: usedFont }); words.push({ text: part, font: usedFont, underline: token.underline });
} }
} }
} }
// Monta linhas respeitando largura máxima // Monta linhas respeitando largura máxima
let lines: { text: string, tokens: { text: string, font: any }[] }[] = []; let lines: { text: string, tokens: { text: string, font: any, underline: boolean }[] }[] = [];
let currentLine = ''; let currentLine = '';
let currentTokens: { text: string, font: any }[] = []; let currentTokens: { text: string, font: any, underline: boolean }[] = [];
let currentWidth = 0; let currentWidth = 0;
for (const word of words) { for (const [i, word] of words.entries()) {
// Aplica recuo de parágrafo na primeira linha
let recuo = 0;
if (indent && lines.length === 0 && currentLine === '') {
recuo = fontSize * 2;
}
const wordWidth = word.font.widthOfTextAtSize(word.text, fontSize); const wordWidth = word.font.widthOfTextAtSize(word.text, fontSize);
if (currentWidth + wordWidth > maxWidth && currentLine.trim() !== '') { if (currentWidth + wordWidth + recuo > maxWidth && currentLine.trim() !== '') {
lines.push({ text: currentLine, tokens: currentTokens }); lines.push({ text: currentLine, tokens: currentTokens });
currentLine = word.text; currentLine = word.text;
currentTokens = [word]; currentTokens = [word];
@@ -403,6 +426,13 @@ export class PdfService {
let drawX = x; let drawX = x;
let totalWidth = line.tokens.reduce((acc, t) => acc + t.font.widthOfTextAtSize(t.text, fontSize), 0); let totalWidth = line.tokens.reduce((acc, t) => acc + t.font.widthOfTextAtSize(t.text, fontSize), 0);
// Aplica recuo de parágrafo na primeira linha
let recuo = 0;
if (indent && i === 0) {
recuo = fontSize * 2;
drawX += recuo;
}
// Centralizado // Centralizado
if (align === 'center') { if (align === 'center') {
drawX = x + (maxWidth - totalWidth) / 2; drawX = x + (maxWidth - totalWidth) / 2;
@@ -420,13 +450,23 @@ export class PdfService {
font: t.font, font: t.font,
color color
}); });
// Sublinhado
if (t.underline && t.text.trim() !== '') {
const underlineY = y - 1.5;
page.drawLine({
start: { x: drawX, y: underlineY },
end: { x: drawX + t.font.widthOfTextAtSize(t.text, fontSize), y: underlineY },
thickness: 0.75,
color
});
}
if (/^\s+$/.test(t.text)) { if (/^\s+$/.test(t.text)) {
drawX += t.font.widthOfTextAtSize(t.text, fontSize) + extraSpace; drawX += t.font.widthOfTextAtSize(t.text, fontSize) + extraSpace;
} else { } else {
drawX += t.font.widthOfTextAtSize(t.text, fontSize); drawX += t.font.widthOfTextAtSize(t.text, fontSize);
} }
} }
y -= fontSize * 1.25; y -= fontSize * 1.5;
continue; continue;
} }
@@ -439,9 +479,19 @@ export class PdfService {
font: t.font, font: t.font,
color color
}); });
// Sublinhado
if (t.underline && t.text.trim() !== '') {
const underlineY = y - 1.5;
page.drawLine({
start: { x: drawX, y: underlineY },
end: { x: drawX + t.font.widthOfTextAtSize(t.text, fontSize), y: underlineY },
thickness: 0.75,
color
});
}
drawX += t.font.widthOfTextAtSize(t.text, fontSize); drawX += t.font.widthOfTextAtSize(t.text, fontSize);
} }
y -= fontSize * 1.25; y -= fontSize * 1.5;
} }
return y; return y;
} }
@@ -512,12 +562,8 @@ export class PdfService {
// Renderiza o parágrafo inteiro com recuo (não só a primeira linha) // Renderiza o parágrafo inteiro com recuo (não só a primeira linha)
let x = margens.left; let x = margens.left;
let parWidth = MAX_WIDTH; let parWidth = MAX_WIDTH;
if (indent) {
x += FONT_SIZE * 2;
parWidth -= FONT_SIZE * 2;
}
// Renderiza texto com formatação inline (negrito, itálico, ambos) e controle de linhas // Renderiza texto com formatação inline (negrito, itálico, sublinhado, ambos) e controle de linhas
let yAfter = renderInlineFormattedParagraph( let yAfter = renderInlineFormattedParagraph(
page, page,
text, text,
@@ -530,12 +576,13 @@ export class PdfService {
FONT_SIZE, FONT_SIZE,
rgb(0, 0, 0), rgb(0, 0, 0),
align, align,
parWidth parWidth,
indent
); );
y = yAfter - FONT_SIZE * 0.5; y = yAfter - FONT_SIZE * 0.5;
currentPar++; currentPar++;
if (y < margens.bottom + FONT_SIZE * 1.25) break; if (y < margens.bottom + FONT_SIZE * 1.5) break;
} }
pageIndex++; pageIndex++;
} }