Representación de texto usando la biblioteca de fuentes | .NET

Descripción general

Si alguna vez quiso tener la función de representar texto en una imagen, el artículo aquí le enseñará cómo hacerlo con fuentes de cualquier formato, compatibles con la biblioteca de fuentes utilizando la solución API Aspose.Font. La biblioteca le permite convertir fácilmente texto en imágenes, así como agregar texto a cualquier imagen.

Representación de texto

Utilice el método DrawText de la clase RenderingUtils para dibujar texto en una imagen. El ejemplo muestra «Hello world» en Verdana y guarda el flujo PNG como Text.png.

Configure DataDir como el directorio que contiene TTF/verdana.ttf y TTF/LatoWeb-Regular.ttf, y OutputDir como un directorio de salida existente. Los ejemplos utilizan los espacios de nombres Aspose.Font, Aspose.Font.Sources, Aspose.Font.Renderers, System, System.IO. En el proyecto de pruebas, BaseTests proporciona los directorios y la configuración de la licencia.

 1// Render text to a PNG image
 2
 3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
 4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
 5string text = "Hello world";
 6string outFile = Path.Combine(OutputDir, "Text.png");
 7using (Stream image = RenderingUtils.DrawText(font, text, 18))
 8{
 9    image.Position = 0;
10    using FileStream output = File.Create(outFile);
11    image.CopyTo(output);
12}
Example-DrawText.cs hosted with ❤ by GitHub

Resultado de la implementación:

Hello world en Verdana

El método DrawText le permite establecer el interlineado y realizar un ajuste automático del texto palabra por palabra.

En el siguiente ejemplo de código, especificamos el tipo de interlineado LineSpacingType.Pixels y lo configuramos en 10, y configuramos el ancho máximo de la imagen en 450.

Todo el texto que no se pueda mostrar correctamente en el rango dado (en nuestro caso es 450) se ajustará en una nueva línea.

El ejemplo repite «Hello world» en Verdana para superar el ancho de 450 píxeles y distribuir el texto en varias líneas. El resultado se guarda como WrappedText.png:

 1// Render wrapped text with pixel line spacing
 2
 3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
 4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
 5string text = "Hello world Hello world Hello world Hello world Hello world Hello world";
 6string outFile = Path.Combine(OutputDir, "WrappedText.png");
 7using (Stream image = RenderingUtils.DrawText(font, text, 22, RenderingUtils.LineSpacingType.Pixels, 10, 450))
 8{
 9    image.Position = 0;
10    using FileStream output = File.Create(outFile);
11    image.CopyTo(output);
12}

Resultado de la implementación:

Texto Hello world en Verdana con ajuste de línea

DrawText() devuelve un flujo PNG. Los ejemplos sitúan su posición al principio, lo copian a un archivo y liberan ambos flujos.

La familia de funciones para el método DrawText() cubre texto estándar o salida de texto multilínea. Pero, en algunos casos, es posible que necesite una salida personalizada del texto, por ejemplo, cuando desee comprimir, estirar, rotar el texto en ángulo o algo más. En este caso, deberá personalizar la salida según sus necesidades.

Representación de texto personalizada

Los siguientes ejemplos dibujan contornos de glifos con System.Drawing.Common en Windows, sobre un lienzo de 960 x 720 píxeles a 300 DPI. Importe también los espacios de nombres Aspose.Font.Glyphs, Aspose.Font.Rendering, Aspose.Font.RenderingPath, System.Drawing, System.Drawing.Drawing2D, System.Drawing.Imaging.

Para dibujar glifos dentro de Aspose.Font se utiliza uno de los métodos RenderGlyph() de la clase GlyphOutlineRenderer. Todos estos métodos sobrecargados se declaran en la interfaz IGlyphRenderer.

Como parámetro debemos pasar a estos métodos una referencia a la fuente, glyphId o índice de glifo y las coordenadas de salida del glifo. Para transferir el último parámetro, se utiliza una matriz especial representada por el objeto Aspose.Font TransformationMatrix. A continuación mostramos cómo usar objetos del tipo TransformationMatrix para pasar coordenadas de salida de glifos a métodos de la familia RenderGlyph().

Entonces, para dibujar un glifo, necesitamos crear un objeto del tipo GlyphOutlineRenderer. Pero un objeto así no puede dibujar un glifo por sí solo. Requiere funcionalidad externa, que se describe en la interfaz IGlyphOutlinePainter. Para utilizar el objeto GlyphOutlineRenderer se debe proporcionar una implementación de IGlyphOutlinePainter.

A continuación puede ver una implementación simple de esta interfaz. Creemos la clase GlyphOutlinePainter que requiere un objeto de tipo System.Drawing.Drawing2D.GraphicsPath para objetivos de dibujo gráfico.

A continuación se muestra la implementación de la interfaz. Añada las sobrecargas de CustomDrawText() y el método FontWidthToImageWidth() mostrados más adelante a la clase GlyphOutlinePainter. El proyecto de ejemplos sitúa estos miembros en una segunda declaración parcial de la misma clase.

 1// Implement a glyph outline painter
 2
 3public partial class GlyphOutlinePainter : IGlyphOutlinePainter
 4{
 5    private readonly GraphicsPath path;
 6    private PointF currentPoint;
 7
 8    public GlyphOutlinePainter(GraphicsPath path)
 9    {
10        this.path = path;
11    }
12
13    public void MoveTo(MoveTo moveTo)
14    {
15        path.CloseFigure();
16        currentPoint = new PointF((float)moveTo.X, (float)moveTo.Y);
17    }
18
19    public void LineTo(LineTo lineTo)
20    {
21        var next = new PointF((float)lineTo.X, (float)lineTo.Y);
22        path.AddLine(currentPoint, next);
23        currentPoint = next;
24    }
25
26    public void CurveTo(CurveTo curveTo)
27    {
28        var next = new PointF((float)curveTo.X3, (float)curveTo.Y3);
29        path.AddBezier(currentPoint, new PointF((float)curveTo.X1, (float)curveTo.Y1),
30            new PointF((float)curveTo.X2, (float)curveTo.Y2), next);
31        currentPoint = next;
32    }
33
34    public void ClosePath()
35    {
36        path.CloseFigure();
37    }
38}

Después de la implementación de IGlyphOutlinePainter, podemos usar objetos de su tipo para representar un glifo individual pasándolos al GlyphOutlineRenderer y llamando a los métodos sobrecargados correspondientes de RenderGlyph() familia.

Para representar una cadena de texto, necesitamos obtener el glyphId para cada carácter y luego, usando un objeto de tipo GlyphOutlineRenderer llamar a uno de los métodos de la familia RenderGlyph(), pasando las coordenadas del glifo correspondiente. Echemos un vistazo a un ejemplo de cómo generar una línea de texto usando la biblioteca Aspose.Font. Se creará en forma de método CustomDrawText(), que acepta parámetros: CustomDrawText(texto de cadena, fuente IFont, tamaño de fuente doble, pincel de fondo, pincel de texto, pincel de salida de cadena).

Cree el método CustomDrawText() que dibuja el texto especificado en el objeto System.Drawing.Bitmap y guarda el mapa de bits resultante en el disco.

Esto incluirá los siguientes pasos:

Pasos auxiliares para esta estrategia

La primera sobrecarga crea y libera un mapa de bits. La segunda acepta uno que pertenece al código que llama al método y admite espaciado, coordenadas, superposiciones y salida vertical. Añada ambas sobrecargas a GlyphOutlinePainter:

 1// Render text with spacing, coordinates, overlays and vertical layout
 2
 3public static void CustomDrawText(string text, IFont font, double fontSize,
 4    Brush backgroundBrush, Brush textBrush, string outFile)
 5{
 6    using var bitmap = new Bitmap(960, 720);
 7    CustomDrawText(text, font, fontSize, backgroundBrush, textBrush, outFile, bitmap);
 8}
 9
10public static void CustomDrawText(string text, IFont font, double fontSize,
11    Brush backgroundBrush, Brush textBrush, string outFile, Bitmap bitmap,
12    double kerningCoefficient = 1, double coordinateX = 0, double coordinateY = 0, bool topDown = false)
13{
14    const double dpi = 300;
15    double scale = fontSize * dpi / 72;
16    bitmap.SetResolution((float)dpi, (float)dpi);
17    using (Graphics graphics = Graphics.FromImage(bitmap))
18    {
19        // A transparent background preserves an existing image for overlays.
20        graphics.FillRectangle(backgroundBrush, 0, 0, bitmap.Width, bitmap.Height);
21        graphics.SmoothingMode = SmoothingMode.HighQuality;
22        GlyphId? previousGid = null;
23        double glyphXCoordinate = coordinateX;
24        double glyphYCoordinate = coordinateY + scale;
25
26        foreach (char character in text)
27        {
28            GlyphId gid = font.Encoding.DecodeToGid(character);
29            if (gid == null) continue;
30            Glyph glyph = font.GlyphAccessor.GetGlyphById(gid);
31            if (glyph == null) continue;
32
33            if (previousGid != null)
34            {
35                if (topDown)
36                    glyphYCoordinate += scale;
37                else
38                {
39                    double advance = FontWidthToImageWidth(font.Metrics.GetGlyphWidth(previousGid),
40                        glyph.SourceResolution, fontSize, dpi);
41                    double kerning = font.Metrics.GetKerningValue(previousGid, gid)
42                        / (double)glyph.SourceResolution * scale;
43                    // Like the source example, the coefficient scales the whole advance.
44                    glyphXCoordinate += (advance + kerning) * kerningCoefficient;
45                }
46            }
47
48            using var path = new GraphicsPath();
49            var painter = new GlyphOutlinePainter(path);
50            var renderer = new GlyphOutlineRenderer(painter);
51            var matrix = new TransformationMatrix(new double[]
52            {
53                scale, 0, 0, -scale, glyphXCoordinate, glyphYCoordinate
54            });
55            renderer.RenderGlyph(font, gid, matrix);
56            path.FillMode = FillMode.Winding;
57            graphics.FillPath(textBrush, path);
58            previousGid = gid;
59        }
60    }
61    // The caller owns the supplied bitmap and may reuse it for another overlay.
62    bitmap.Save(outFile, ImageFormat.Png);
63}

Añada a la misma clase el método auxiliar que convierte el ancho de unidades de fuente a píxeles:

1// Convert font units to image pixels
2
3private static double FontWidthToImageWidth(double width, int fontSourceResolution,
4    double fontSize, double dpi = 300)
5{
6    return width / fontSourceResolution * fontSize * dpi / 72;
7}

El ejemplo muestra «Hello world» en Verdana negro sobre blanco y en Lato azul sobre amarillo. Guarda Custom_Verdana.png y Custom_Lato.png:

 1// Render two fonts with a custom glyph painter
 2
 3string verdanaPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
 4string latoPath = Path.Combine(DataDir, "TTF", "LatoWeb-Regular.ttf");
 5Font verdana = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(verdanaPath)));
 6Font lato = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(latoPath)));
 7string verdanaOutput = Path.Combine(OutputDir, "Custom_Verdana.png");
 8string latoOutput = Path.Combine(OutputDir, "Custom_Lato.png");
 9GlyphOutlinePainter.CustomDrawText("Hello world", verdana, 24, Brushes.White, Brushes.Black, verdanaOutput);
10GlyphOutlinePainter.CustomDrawText("Hello world", lato, 24, Brushes.Yellow, Brushes.Blue, latoOutput);

Resultado de la implementación:

Renderizado personalizado del texto en Verdana Verdana

Renderizado personalizado del texto en Lato Lato

Interletraje

El método obtiene el kerning de pares de la fuente. kerningCoefficient: 1.25 aumenta un 25 % todo el avance horizontal (ancho del glifo anterior más kerning del par). El ejemplo utiliza Verdana y guarda IncreasedSpacing.png:

1// Increase the horizontal glyph advance
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string outFile = Path.Combine(OutputDir, "IncreasedSpacing.png");
6using var bitmap = new Bitmap(960, 720);
7GlyphOutlinePainter.CustomDrawText("Hello world", font, 18, Brushes.White, Brushes.Black,
8    outFile, bitmap, kerningCoefficient: 1.25);

Resultado de la implementación:

Verdana con un coeficiente de espaciado de 1.25 Verdana con un coeficiente de espaciado de 1.25

Representar texto por coordenadas

Posicione el texto con coordinateX y coordinateY. Para obtener la primera línea base, el método suma fontSize * 300 / 72 píxeles a coordinateY. A 18 puntos, coordinateX: 300 y coordinateY: 225 la sitúan en (300, 300). El resultado se guarda como PositionedText.png:

1// Position text at explicit coordinates
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string outFile = Path.Combine(OutputDir, "PositionedText.png");
6using var bitmap = new Bitmap(960, 720);
7// At 18 points and 300 DPI the 75-pixel baseline offset makes the baseline Y=300.
8GlyphOutlinePainter.CustomDrawText("Hello world", font, 18, Brushes.White, Brushes.Black,
9    outFile, bitmap, coordinateX: 300, coordinateY: 225);

Resultado de la implementación:

Verdana, primera línea base en X=300 e Y=300 Verdana, primera línea base en X=300 e Y=300

Cómo agregar el texto a una imagen.

Para añadir texto a una imagen existente, pase un mapa de bits y un pincel de fondo transparente al método común. El pincel transparente conserva los píxeles existentes.

La sobrecarga de CustomDrawText() que acepta un mapa de bits ya admite este escenario. Dibuja sobre la imagen recibida y la guarda como PNG; la imagen sigue perteneciendo al código que llama al método.

El ejemplo crea OverlayBase.png en OutputDir, abre esa imagen y añade una sombra gris y una anotación marrón. Guarda por separado Shadow.png y ShadowAnnotated.png; no necesita una imagen de entrada externa:

 1// Add a shadow and annotation to an existing image
 2
 3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
 4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
 5string inputImagePath = Path.Combine(OutputDir, "OverlayBase.png");
 6GlyphOutlinePainter.CustomDrawText("Hello world", font, 17, Brushes.White, Brushes.Black, inputImagePath);
 7using var bitmap = new Bitmap(inputImagePath);
 8string shadowFile = Path.Combine(OutputDir, "Shadow.png");
 9GlyphOutlinePainter.CustomDrawText("Hello world", font, 17, Brushes.Transparent, Brushes.Gray,
10    shadowFile, bitmap, coordinateX: -3);
11string annotatedFile = Path.Combine(OutputDir, "ShadowAnnotated.png");
12GlyphOutlinePainter.CustomDrawText("<= Shadow effect", font, 17, Brushes.Transparent, Brushes.Brown,
13    annotatedFile, bitmap, coordinateX: 400);

Resultado de la implementación:

Verdana con sombra Verdana con sombra

Verdana con sombra y anotación. La anotación queda recortada en el borde derecho del lienzo de 960 píxeles de ancho. Verdana con sombra y anotación. La anotación queda recortada en el borde derecho del lienzo de 960 píxeles de ancho.

Salida del texto de arriba a abajo

La misma sobrecarga permite la salida vertical con topDown: true. Sitúa el primer glifo en la línea base inicial y desplaza cada glifo siguiente fontSize * 300 / 72 píxeles hacia abajo.

El ejemplo muestra «Top down» en Verdana en X=400 y guarda TopDown.png. Con coordinateY igual a cero y un tamaño de 18 puntos, la primera línea base se encuentra en Y=75 píxeles:

1// Render text vertically from top to bottom
2
3string fontPath = Path.Combine(DataDir, "TTF", "verdana.ttf");
4Font font = Font.Open(new FontDefinition(FontType.TTF, "ttf", new FileSystemStreamSource(fontPath)));
5string outFile = Path.Combine(OutputDir, "TopDown.png");
6using var bitmap = new Bitmap(960, 720);
7GlyphOutlinePainter.CustomDrawText("Top down", font, 18, Brushes.White, Brushes.Black,
8    outFile, bitmap, coordinateX: 400, topDown: true);
Example-RenderTopDown.cs hosted with ❤ by GitHub

Resultado de la implementación:

Verdana, de arriba abajo Verdana, de arriba abajo

información adicional

El método utiliza 300 DPI. scale = fontSize * dpi / 72 convierte el tamaño de fuente de puntos a píxeles. glyphXCoordinate y glyphYCoordinate definen la posición de la línea base del glifo. En modo horizontal, el siguiente avance incluye el ancho del glifo anterior y el kerning del par obtenido de la fuente; kerningCoefficient multiplica su suma.