@cognima/banners 0.0.1-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/fonts/Manrope/Manrope-Bold.ttf +0 -0
- package/assets/fonts/Manrope/Manrope-Regular.ttf +0 -0
- package/assets/fonts/Others/AbyssinicaSIL-Regular.ttf +0 -0
- package/assets/fonts/Others/ChirpRegular.ttf +0 -0
- package/assets/fonts/Poppins/Poppins-Bold.ttf +0 -0
- package/assets/fonts/Poppins/Poppins-Medium.ttf +0 -0
- package/assets/fonts/Poppins/Poppins-Regular.ttf +0 -0
- package/assets/placeholders/album_art.png +0 -0
- package/assets/placeholders/avatar.png +0 -0
- package/assets/placeholders/badge.jpg +0 -0
- package/assets/placeholders/badge.png +0 -0
- package/assets/placeholders/badge_2.jpg +0 -0
- package/assets/placeholders/badge_3.jpg +0 -0
- package/assets/placeholders/badge_4.jpg +0 -0
- package/assets/placeholders/badge_5.jpg +0 -0
- package/assets/placeholders/banner.jpeg +0 -0
- package/assets/placeholders/images.jpeg +0 -0
- package/index.js +153 -0
- package/package.json +34 -0
- package/src/animation-effects.js +631 -0
- package/src/cache-manager.js +258 -0
- package/src/community-banner.js +1536 -0
- package/src/constants.js +208 -0
- package/src/discord-profile.js +584 -0
- package/src/e-commerce-banner.js +1214 -0
- package/src/effects.js +355 -0
- package/src/error-handler.js +305 -0
- package/src/event-banner.js +1319 -0
- package/src/facebook-post.js +679 -0
- package/src/gradient-welcome.js +430 -0
- package/src/image-filters.js +1034 -0
- package/src/image-processor.js +1014 -0
- package/src/instagram-post.js +504 -0
- package/src/interactive-elements.js +1208 -0
- package/src/linkedin-post.js +658 -0
- package/src/marketing-banner.js +1089 -0
- package/src/minimalist-banner.js +892 -0
- package/src/modern-profile.js +755 -0
- package/src/performance-optimizer.js +216 -0
- package/src/telegram-header.js +544 -0
- package/src/test-runner.js +645 -0
- package/src/tiktok-post.js +713 -0
- package/src/twitter-header.js +604 -0
- package/src/validator.js +442 -0
- package/src/welcome-leave.js +445 -0
- package/src/whatsapp-status.js +386 -0
- package/src/youtube-thumbnail.js +681 -0
- package/utils.js +710 -0
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Módulo de Banner de Boas-vindas/Saída
|
|
5
|
+
*
|
|
6
|
+
* Este módulo gera banners de boas-vindas ou despedida com avatar centralizado
|
|
7
|
+
* e mensagem personalizada.
|
|
8
|
+
*
|
|
9
|
+
* @author Cognima Team (melhorado)
|
|
10
|
+
* @version 2.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
|
|
15
|
+
const pureimage = require("pureimage");
|
|
16
|
+
const path = require("path");
|
|
17
|
+
const {
|
|
18
|
+
loadImageWithAxios,
|
|
19
|
+
encodeToBuffer,
|
|
20
|
+
roundRect,
|
|
21
|
+
wrapText,
|
|
22
|
+
registerFontIfNeeded,
|
|
23
|
+
isValidHexColor,
|
|
24
|
+
DEFAULT_FONT_FAMILY,
|
|
25
|
+
applyTextShadow,
|
|
26
|
+
clearShadow,
|
|
27
|
+
createLinearGradient,
|
|
28
|
+
hexToRgba
|
|
29
|
+
} = require("../utils");
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @class WelcomeLeave
|
|
33
|
+
* @classdesc Gera um banner de boas-vindas ou despedida visualmente refinado com avatar central.
|
|
34
|
+
* @example const welcomeCard = new WelcomeLeave()
|
|
35
|
+
* .setAvatar("avatar.png")
|
|
36
|
+
* .setTitle("Bem-vindo!")
|
|
37
|
+
* .setUsername("NovoUsuário")
|
|
38
|
+
* .setMessage("Ficamos felizes em ter você aqui!")
|
|
39
|
+
* .build();
|
|
40
|
+
*/
|
|
41
|
+
module.exports = class WelcomeLeave {
|
|
42
|
+
constructor(options) {
|
|
43
|
+
// Dados Principais
|
|
44
|
+
this.avatar = null;
|
|
45
|
+
this.title = "Bem-vindo!";
|
|
46
|
+
this.username = "Usuário";
|
|
47
|
+
this.message = "Texto da mensagem aqui.";
|
|
48
|
+
|
|
49
|
+
// Personalização Visual
|
|
50
|
+
this.font = { name: options?.font?.name ?? DEFAULT_FONT_FAMILY, path: options?.font?.path };
|
|
51
|
+
this.background = { type: "color", value: "#2C2F33" };
|
|
52
|
+
this.titleColor = "#FFFFFF";
|
|
53
|
+
this.usernameColor = "#FFFFFF";
|
|
54
|
+
this.messageColor = "#B9BBBE";
|
|
55
|
+
this.avatarBorderColor = null;
|
|
56
|
+
this.cardBorderColor = null;
|
|
57
|
+
this.overlayOpacity = 0.3;
|
|
58
|
+
|
|
59
|
+
// Novas opções de personalização
|
|
60
|
+
this.useTextShadow = false;
|
|
61
|
+
this.useGradient = false;
|
|
62
|
+
this.gradientColors = { start: "#3498db", end: "#8e44ad" };
|
|
63
|
+
this.gradientDirection = "vertical";
|
|
64
|
+
this.cornerRadius = 0; // Raio dos cantos arredondados
|
|
65
|
+
this.avatarBorderWidth = 4; // Largura da borda do avatar
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- Setters para Dados Principais ---
|
|
69
|
+
/**
|
|
70
|
+
* Define a imagem do avatar
|
|
71
|
+
* @param {string|Buffer|Object} image - URL, Buffer ou caminho da imagem do avatar
|
|
72
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
73
|
+
*/
|
|
74
|
+
setAvatar(image) {
|
|
75
|
+
if (!image) throw new Error("A fonte da imagem do avatar não pode estar vazia.");
|
|
76
|
+
this.avatar = image;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Define o título do banner
|
|
82
|
+
* @param {string} text - Texto do título
|
|
83
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
84
|
+
*/
|
|
85
|
+
setTitle(text) {
|
|
86
|
+
if (!text || typeof text !== "string") throw new Error("O texto do título deve ser uma string não vazia.");
|
|
87
|
+
this.title = text;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Define o nome de usuário
|
|
93
|
+
* @param {string} name - Nome do usuário
|
|
94
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
95
|
+
*/
|
|
96
|
+
setUsername(name) {
|
|
97
|
+
if (!name || typeof name !== "string") throw new Error("O nome de usuário deve ser uma string não vazia.");
|
|
98
|
+
this.username = name;
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Define o texto da mensagem
|
|
104
|
+
* @param {string} text - Texto da mensagem
|
|
105
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
106
|
+
*/
|
|
107
|
+
setMessage(text) {
|
|
108
|
+
if (!text || typeof text !== "string") throw new Error("O texto da mensagem deve ser uma string não vazia.");
|
|
109
|
+
this.message = text;
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// --- Setters para Personalização Visual ---
|
|
114
|
+
/**
|
|
115
|
+
* Define o plano de fundo do banner
|
|
116
|
+
* @param {string} type - Tipo de plano de fundo ('color' ou 'image')
|
|
117
|
+
* @param {string} value - Valor do plano de fundo (cor hexadecimal ou URL/caminho da imagem)
|
|
118
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
119
|
+
*/
|
|
120
|
+
setBackground(type, value) {
|
|
121
|
+
const types = ["color", "image"];
|
|
122
|
+
if (!type || !types.includes(type.toLowerCase())) throw new Error("O tipo de plano de fundo deve ser 'color' ou 'image'.");
|
|
123
|
+
if (!value) throw new Error("O valor do plano de fundo não pode estar vazio.");
|
|
124
|
+
if (type.toLowerCase() === "color" && !isValidHexColor(value)) throw new Error("Cor de plano de fundo inválida. Use o formato hexadecimal.");
|
|
125
|
+
this.background = { type: type.toLowerCase(), value };
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Define a cor do título
|
|
131
|
+
* @param {string} color - Cor hexadecimal
|
|
132
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
133
|
+
*/
|
|
134
|
+
setTitleColor(color) {
|
|
135
|
+
if (!color || !isValidHexColor(color)) throw new Error("Cor de título inválida. Use o formato hexadecimal.");
|
|
136
|
+
this.titleColor = color;
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Define a cor do nome de usuário
|
|
142
|
+
* @param {string} color - Cor hexadecimal
|
|
143
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
144
|
+
*/
|
|
145
|
+
setUsernameColor(color) {
|
|
146
|
+
if (!color || !isValidHexColor(color)) throw new Error("Cor de nome de usuário inválida. Use o formato hexadecimal.");
|
|
147
|
+
this.usernameColor = color;
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Define a cor da mensagem
|
|
153
|
+
* @param {string} color - Cor hexadecimal
|
|
154
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
155
|
+
*/
|
|
156
|
+
setMessageColor(color) {
|
|
157
|
+
if (!color || !isValidHexColor(color)) throw new Error("Cor de mensagem inválida. Use o formato hexadecimal.");
|
|
158
|
+
this.messageColor = color;
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Define a cor da borda do avatar
|
|
164
|
+
* @param {string} color - Cor hexadecimal
|
|
165
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
166
|
+
*/
|
|
167
|
+
setAvatarBorderColor(color) {
|
|
168
|
+
if (color && !isValidHexColor(color)) throw new Error("Cor de borda do avatar inválida. Use o formato hexadecimal.");
|
|
169
|
+
this.avatarBorderColor = color;
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Define a cor da borda do card
|
|
175
|
+
* @param {string} color - Cor hexadecimal
|
|
176
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
177
|
+
*/
|
|
178
|
+
setCardBorderColor(color) {
|
|
179
|
+
if (color && !isValidHexColor(color)) throw new Error("Cor de borda do card inválida. Use o formato hexadecimal.");
|
|
180
|
+
this.cardBorderColor = color;
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Define a opacidade da sobreposição
|
|
186
|
+
* @param {number} opacity - Valor de opacidade (0-1)
|
|
187
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
188
|
+
*/
|
|
189
|
+
setOverlayOpacity(opacity) {
|
|
190
|
+
if (typeof opacity !== "number" || opacity < 0 || opacity > 1) throw new Error("A opacidade da sobreposição deve estar entre 0 e 1.");
|
|
191
|
+
this.overlayOpacity = opacity;
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Ativa ou desativa a sombra de texto
|
|
197
|
+
* @param {boolean} enabled - Se a sombra de texto deve ser ativada
|
|
198
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
199
|
+
*/
|
|
200
|
+
enableTextShadow(enabled = true) {
|
|
201
|
+
this.useTextShadow = enabled;
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Ativa ou desativa o gradiente de fundo
|
|
207
|
+
* @param {boolean} enabled - Se o gradiente deve ser ativado
|
|
208
|
+
* @param {string} startColor - Cor inicial do gradiente (hexadecimal)
|
|
209
|
+
* @param {string} endColor - Cor final do gradiente (hexadecimal)
|
|
210
|
+
* @param {string} direction - Direção do gradiente ('vertical', 'horizontal', 'diagonal')
|
|
211
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
212
|
+
*/
|
|
213
|
+
enableGradient(enabled = true, startColor = "#3498db", endColor = "#8e44ad", direction = "vertical") {
|
|
214
|
+
this.useGradient = enabled;
|
|
215
|
+
|
|
216
|
+
if (startColor && isValidHexColor(startColor)) {
|
|
217
|
+
this.gradientColors.start = startColor;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (endColor && isValidHexColor(endColor)) {
|
|
221
|
+
this.gradientColors.end = endColor;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const validDirections = ["vertical", "horizontal", "diagonal"];
|
|
225
|
+
if (direction && validDirections.includes(direction.toLowerCase())) {
|
|
226
|
+
this.gradientDirection = direction.toLowerCase();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Define o raio dos cantos arredondados
|
|
234
|
+
* @param {number} radius - Raio dos cantos em pixels
|
|
235
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
236
|
+
*/
|
|
237
|
+
setCornerRadius(radius) {
|
|
238
|
+
if (typeof radius !== "number" || radius < 0) throw new Error("O raio dos cantos deve ser um número não negativo.");
|
|
239
|
+
this.cornerRadius = radius;
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Define a largura da borda do avatar
|
|
245
|
+
* @param {number} width - Largura da borda em pixels
|
|
246
|
+
* @returns {WelcomeLeave} - Instância atual para encadeamento
|
|
247
|
+
*/
|
|
248
|
+
setAvatarBorderWidth(width) {
|
|
249
|
+
if (typeof width !== "number" || width < 0) throw new Error("A largura da borda do avatar deve ser um número não negativo.");
|
|
250
|
+
this.avatarBorderWidth = width;
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// --- Método de Construção ---
|
|
255
|
+
/**
|
|
256
|
+
* Constrói o banner e retorna um buffer de imagem
|
|
257
|
+
* @returns {Promise<Buffer>} - Buffer contendo a imagem do banner
|
|
258
|
+
*/
|
|
259
|
+
async build() {
|
|
260
|
+
if (!this.avatar) throw new Error("A imagem do avatar deve ser definida usando setAvatar().");
|
|
261
|
+
|
|
262
|
+
// --- Registro de Fonte ---
|
|
263
|
+
const registeredFontName = await registerFontIfNeeded(this.font);
|
|
264
|
+
|
|
265
|
+
// --- Configuração do Canvas ---
|
|
266
|
+
const cardWidth = 700;
|
|
267
|
+
const cardHeight = 350;
|
|
268
|
+
const avatarSize = 128;
|
|
269
|
+
const borderRadius = this.cornerRadius;
|
|
270
|
+
const padding = 30;
|
|
271
|
+
|
|
272
|
+
const canvas = pureimage.make(cardWidth, cardHeight);
|
|
273
|
+
const ctx = canvas.getContext("2d");
|
|
274
|
+
|
|
275
|
+
// --- Desenha Plano de Fundo e Sobreposição ---
|
|
276
|
+
ctx.save();
|
|
277
|
+
roundRect(ctx, 0, 0, cardWidth, cardHeight, borderRadius, false, false);
|
|
278
|
+
ctx.clip();
|
|
279
|
+
ctx.globalAlpha = 1;
|
|
280
|
+
|
|
281
|
+
if (this.useGradient && this.background.type === "color") {
|
|
282
|
+
// Aplica gradiente como plano de fundo
|
|
283
|
+
const gradient = createLinearGradient(
|
|
284
|
+
ctx,
|
|
285
|
+
0,
|
|
286
|
+
0,
|
|
287
|
+
cardWidth,
|
|
288
|
+
cardHeight,
|
|
289
|
+
this.gradientColors.start,
|
|
290
|
+
this.gradientColors.end,
|
|
291
|
+
this.gradientDirection
|
|
292
|
+
);
|
|
293
|
+
ctx.fillStyle = gradient;
|
|
294
|
+
ctx.fillRect(0, 0, cardWidth, cardHeight);
|
|
295
|
+
} else if (this.background.type === "color") {
|
|
296
|
+
// Plano de fundo de cor sólida
|
|
297
|
+
ctx.fillStyle = this.background.value;
|
|
298
|
+
ctx.fillRect(0, 0, cardWidth, cardHeight);
|
|
299
|
+
} else {
|
|
300
|
+
// Plano de fundo de imagem
|
|
301
|
+
try {
|
|
302
|
+
const img = await loadImageWithAxios(this.background.value);
|
|
303
|
+
const aspect = img.width / img.height;
|
|
304
|
+
let drawWidth = cardWidth;
|
|
305
|
+
let drawHeight = cardWidth / aspect;
|
|
306
|
+
|
|
307
|
+
// Ajusta as dimensões para cobrir todo o card
|
|
308
|
+
if (drawHeight < cardHeight) {
|
|
309
|
+
drawHeight = cardHeight;
|
|
310
|
+
drawWidth = cardHeight * aspect;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const offsetX = (cardWidth - drawWidth) / 2;
|
|
314
|
+
const offsetY = (cardHeight - drawHeight) / 2;
|
|
315
|
+
|
|
316
|
+
ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
|
|
317
|
+
} catch (e) {
|
|
318
|
+
console.error("Falha ao desenhar imagem de plano de fundo:", e.message);
|
|
319
|
+
ctx.fillStyle = "#2C2F33";
|
|
320
|
+
ctx.fillRect(0, 0, cardWidth, cardHeight);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Aplica sobreposição para melhorar legibilidade do texto
|
|
325
|
+
if (this.overlayOpacity > 0 && this.background.type === "image") {
|
|
326
|
+
ctx.globalAlpha = this.overlayOpacity;
|
|
327
|
+
ctx.fillStyle = "#000000";
|
|
328
|
+
ctx.fillRect(0, 0, cardWidth, cardHeight);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
ctx.restore();
|
|
332
|
+
ctx.globalAlpha = 1;
|
|
333
|
+
|
|
334
|
+
// --- Desenha Borda do Card (Opcional) ---
|
|
335
|
+
if (this.cardBorderColor) {
|
|
336
|
+
ctx.strokeStyle = this.cardBorderColor;
|
|
337
|
+
ctx.lineWidth = 5;
|
|
338
|
+
roundRect(ctx, ctx.lineWidth / 2, ctx.lineWidth / 2, cardWidth - ctx.lineWidth, cardHeight - ctx.lineWidth, borderRadius - ctx.lineWidth / 2, false, true);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// --- Desenha Avatar ---
|
|
342
|
+
const avatarX = cardWidth / 2 - avatarSize / 2;
|
|
343
|
+
const avatarY = padding + 20;
|
|
344
|
+
|
|
345
|
+
ctx.save();
|
|
346
|
+
ctx.beginPath();
|
|
347
|
+
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
|
|
348
|
+
ctx.closePath();
|
|
349
|
+
ctx.clip();
|
|
350
|
+
|
|
351
|
+
try {
|
|
352
|
+
const avatarImg = await loadImageWithAxios(this.avatar);
|
|
353
|
+
ctx.drawImage(avatarImg, avatarX, avatarY, avatarSize, avatarSize);
|
|
354
|
+
} catch (e) {
|
|
355
|
+
console.error("Falha ao desenhar imagem do avatar:", e.message);
|
|
356
|
+
ctx.fillStyle = "#555";
|
|
357
|
+
ctx.fillRect(avatarX, avatarY, avatarSize, avatarSize);
|
|
358
|
+
ctx.fillStyle = "#FFF";
|
|
359
|
+
ctx.font = `bold 30px ${registeredFontName}-Bold`;
|
|
360
|
+
ctx.textAlign = "center";
|
|
361
|
+
ctx.textBaseline = "middle";
|
|
362
|
+
ctx.fillText("?", avatarX + avatarSize / 2, avatarY + avatarSize / 2);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
ctx.restore();
|
|
366
|
+
|
|
367
|
+
// --- Desenha Borda do Avatar (Opcional) ---
|
|
368
|
+
if (this.avatarBorderColor) {
|
|
369
|
+
ctx.strokeStyle = this.avatarBorderColor;
|
|
370
|
+
ctx.lineWidth = this.avatarBorderWidth;
|
|
371
|
+
ctx.beginPath();
|
|
372
|
+
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2 + ctx.lineWidth / 2, 0, Math.PI * 2);
|
|
373
|
+
ctx.stroke();
|
|
374
|
+
ctx.closePath();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// --- Desenha Texto --- (Centralizado abaixo do avatar)
|
|
378
|
+
const textYStart = avatarY + avatarSize + 30;
|
|
379
|
+
const textMaxWidth = cardWidth - 2 * padding;
|
|
380
|
+
|
|
381
|
+
// Título
|
|
382
|
+
ctx.fillStyle = this.titleColor;
|
|
383
|
+
ctx.font = `bold 32px ${registeredFontName}-Bold`;
|
|
384
|
+
ctx.textAlign = "center";
|
|
385
|
+
ctx.textBaseline = "top";
|
|
386
|
+
|
|
387
|
+
// Aplica sombra de texto se ativada
|
|
388
|
+
if (this.useTextShadow) {
|
|
389
|
+
applyTextShadow(ctx);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const titleText = this.title.length > 30 ? this.title.slice(0, 27) + "..." : this.title;
|
|
393
|
+
wrapText(ctx, titleText, cardWidth / 2, textYStart, textMaxWidth - 40, 28, registeredFontName)
|
|
394
|
+
|
|
395
|
+
// Remove sombra para o próximo texto
|
|
396
|
+
if (this.useTextShadow) {
|
|
397
|
+
clearShadow(ctx);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Nome de Usuário (Abaixo do Título)
|
|
401
|
+
ctx.fillStyle = this.usernameColor;
|
|
402
|
+
ctx.font = `medium 28px ${registeredFontName}-Medium`;
|
|
403
|
+
ctx.textAlign = "center";
|
|
404
|
+
const usernameY = textYStart + 40;
|
|
405
|
+
|
|
406
|
+
// Aplica sombra de texto se ativada
|
|
407
|
+
if (this.useTextShadow) {
|
|
408
|
+
applyTextShadow(ctx);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const usernameText = this.username.length > 35 ? this.username.slice(0, 32) + "..." : this.username;
|
|
412
|
+
|
|
413
|
+
// Remove sombra para o próximo texto
|
|
414
|
+
if (this.useTextShadow) {
|
|
415
|
+
clearShadow(ctx);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Mensagem (Abaixo do Nome de Usuário)
|
|
419
|
+
ctx.fillStyle = this.messageColor;
|
|
420
|
+
ctx.font = `regular 22px ${registeredFontName}-Regular`;
|
|
421
|
+
ctx.textAlign = "center";
|
|
422
|
+
const messageY = usernameY + 35;
|
|
423
|
+
|
|
424
|
+
// Aplica sombra de texto se ativada
|
|
425
|
+
if (this.useTextShadow) {
|
|
426
|
+
applyTextShadow(ctx, "#000000", 2, 1, 1);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
wrapText(ctx, this.message, cardWidth / 2, messageY, textMaxWidth - 40, 28, registeredFontName);
|
|
430
|
+
|
|
431
|
+
// Remove sombra
|
|
432
|
+
if (this.useTextShadow) {
|
|
433
|
+
clearShadow(ctx);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// --- Codifica e Retorna Buffer ---
|
|
437
|
+
try {
|
|
438
|
+
return await encodeToBuffer(canvas);
|
|
439
|
+
} catch (err) {
|
|
440
|
+
console.error("Falha ao codificar o card de Boas-vindas/Saída:", err);
|
|
441
|
+
throw new Error("Não foi possível gerar o buffer de imagem do card de Boas-vindas/Saída.");
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|