En el desarrollo de sistemas de publicación y gestión de contenidos, es un requerimiento frecuente la generación automática de miniaturas o banners. Estos recursos gráficos suelen utilizar una plantilla de fondo sobre la cual se debe superponer texto dinámico, como títulos de cursos o nombres de proyectos. Automatizar este proceso mediante código elimina la dependencia de diseño gráfico manual para cada instancia y permite escalar la producción de activos visuales.
El desafío principal radica en adaptar el tamaño de la fuente y aplicar saltos de línea automáticamente cuando el texto excede el área designada dentro de la imagen de fondo.
Entorno de Desarrollo
- Sistema Operativo: Windows Server 2019 o superior.
- Framwork: .NET Framework 4.0+ o .NET Core/.NET 5+ (requiere el paquete
System.Drawing.Common). - IDE: Visual Studio 2019 / 2022.
Diseño de la Solución
Para mejorar la robustez, el rendimiento y la mantenibilidad del código, se refactorizará la lógica original. En lugar de pasar coordenadas como cadenas de texto separadas por comas, se utilizará la estructura nativa RectangleF para definir el área de renderizado. Además, se implementará un manejo estricto de recursos no administrados (GDI+) mediante bloques using para prevenir fugas de memoria.
Implementación del Código
Renderizado de Texto en Imagen
El siguiente método carga la plantilla, calcula dinámicamente el tamaño de la fuente si el texto es demasiado largo y lo dibuja en el área especificada con alineación central.
public Bitmap RenderTextOnTemplate(string templatePath, string outputPath, RectangleF textArea, string content, string fontFamily = "Arial")
{
using (Image templateImage = Image.FromFile(templatePath))
using (Bitmap resultBitmap = new Bitmap(templateImage.Width, templateImage.Height))
using (Graphics graphics = Graphics.FromImage(resultBitmap))
{
// Dibujar la imagen de fondo
graphics.DrawImage(templateImage, 0, 0, templateImage.Width, templateImage.Height);
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
// Configuración inicial de la fuente y formato de texto
float fontSize = textArea.Height * 0.8f;
StringFormat format = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center,
FormatFlags = StringFormatFlags.LineLimit
};
using (SolidBrush brush = new SolidBrush(Color.White))
{
// Ajustar dinámicamente el tamaño de la fuente si el texto excede el área
while (fontSize > 8)
{
using (Font currentFont = new Font(fontFamily, fontSize, FontStyle.Bold, GraphicsUnit.Pixel))
{
SizeF textSize = graphics.MeasureString(content, currentFont, (int)textArea.Width, format);
if (textSize.Height <= textArea.Height)
{
graphics.DrawString(content, currentFont, brush, textArea, format);
break;
}
}
fontSize -= 1; // Reducir tamaño de fuente iterativamente
}
}
// Guardar la imagen resultante
resultBitmap.Save(outputPath, System.Drawing.Imaging.ImageFormat.Jpeg);
// Retornar un clon para que el llamador pueda disponer de él independientemente
return (Bitmap)resultBitmap.Clone();
}
}
Conversión de Imagen a Base64
Para integrar la imagen generada directamente en interfaces web sin necesidad de servir archivos físicos adicionales, se convierte el resultado a una cadena Base64. Se optimiza la lectura de bytes y la resolución de tipos MIME.
public string ConvertImageToBase64(string imagePath, bool includeDataUriScheme = false)
{
if (!File.Exists(imagePath)) return string.Empty;
byte[] imageBytes = File.ReadAllBytes(imagePath);
string base64String = Convert.ToBase64String(imageBytes);
if (includeDataUriScheme)
{
string extension = Path.GetExtension(imagePath).TrimStart('.').ToLowerInvariant();
string mimeType = GetMimeType(extension);
return $"data:{mimeType};base64,{base64String}";
}
return base64String;
}
private string GetMimeType(string extension)
{
var mimeTypes = new Dictionary<string, string>
{
{ "jpg", "image/jpeg" }, { "jpeg", "image/jpeg" },
{ "png", "image/png" }, { "gif", "image/gif" },
{ "bmp", "image/bmp" }, { "tiff", "image/tiff" }
};
return mimeTypes.TryGetValue(extension, out var mimeType) ? mimeType : "image/jpeg";
}
Ejemplo de Integración en ASP.NET WebForms
A continuación se muestra cómo invocar los métodos anteriores en el ciclo de vida de una página web para generar y mostrar la imagen dinámicamente.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string baseDirectory = Server.MapPath("~/assets/images/");
string templateFile = Path.Combine(baseDirectory, "template_bg.jpg");
string outputFile = Path.Combine(baseDirectory, "generated_output.jpg");
string dynamicTitle = "Arquitectura de Microservicios con .NET y Docker";
// Definir el área de texto (X, Y, Ancho, Alto)
RectangleF textBounds = new RectangleF(20, 50, 400, 120);
using (Bitmap generatedImage = RenderTextOnTemplate(templateFile, outputFile, textBounds, dynamicTitle))
{
// Asignar la imagen generada a un control Image de ASP.NET
imgPreview.ImageUrl = ConvertImageToBase64(outputFile, includeDataUriScheme: true);
}
}
}