Rendu de texte à l'aide de la bibliothèque de polices | .NET

Aperçu

Si vous avez toujours souhaité pouvoir restituer du texte dans une image, l’article ici vous apprendra comment le faire avec des polices de n’importe quel format, prises en charge par la bibliothèque de polices utilisant la solution API Aspose.Font. La bibliothèque vous permet de convertir facilement du texte en images et d’ajouter du texte à n’importe quelle image.

Rendu du texte

Utilisez la méthode DrawText de la classe RenderingUtils pour dessiner du texte dans une image. L’exemple affiche « Hello world » en Verdana et enregistre le flux PNG dans Text.png.

Définissez DataDir sur le répertoire contenant TTF/verdana.ttf et TTF/LatoWeb-Regular.ttf, et OutputDir sur un répertoire de sortie existant. Les exemples utilisent les espaces de noms Aspose.Font, Aspose.Font.Sources, Aspose.Font.Renderers, System, System.IO. Dans le projet de tests, BaseTests fournit ces répertoires et la configuration de la licence.

 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

Résultat de l’implémentation :

Hello world en Verdana

La méthode DrawText vous permet de définir l’espacement des lignes et d’effectuer un habillage automatique du texte mot par mot.

Dans l’exemple de code ci-dessous, nous avons spécifié le type d’espacement des lignes LineSpacingType.Pixels et l’avons défini sur 10, et nous avons défini la largeur maximale de l’image sur 450.

Tout le texte qui ne peut pas être affiché correctement dans la plage donnée (dans notre cas, il s’agit de 450) sera renvoyé sur une nouvelle ligne.

L’exemple répète « Hello world » en Verdana pour dépasser la largeur de 450 pixels et répartir le texte sur plusieurs lignes. Le résultat est enregistré dans 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}

Résultat de l’implémentation :

Texte Hello world en Verdana avec retour à la ligne

DrawText() renvoie un flux PNG. Les exemples replacent sa position au début, le copient dans un fichier, puis libèrent les deux flux.

La famille de fonctions pour la méthode DrawText() couvre la sortie de texte standard ou de texte multiligne. Mais, dans certains cas, vous aurez peut-être besoin d’une sortie personnalisée du texte, par exemple lorsque vous souhaitez compresser, étirer, faire pivoter le texte selon un angle ou autre chose. Dans ce cas, vous devez personnaliser la sortie en fonction de vos besoins.

Rendu de texte personnalisé

Les exemples suivants dessinent les contours des glyphes avec System.Drawing.Common sous Windows, sur une surface de 960 x 720 pixels à 300 DPI. Importez également les espaces de noms Aspose.Font.Glyphs, Aspose.Font.Rendering, Aspose.Font.RenderingPath, System.Drawing, System.Drawing.Drawing2D, System.Drawing.Imaging.

Pour dessiner des glyphs dans Aspose.Font, utilisez l’une des méthodes RenderGlyph() de la classe GlyphOutlineRenderer. Toutes ces méthodes surchargées sont déclarées dans l’interface IGlyphRenderer.

En paramètre, nous devons passer à ces méthodes une référence à la police, au glyphId ou à l’index du glyphe, et aux coordonnées de sortie du glyphe. Pour transférer le dernier paramètre, une matrice spéciale représentée par l’objet Aspose.Font TransformationMatrix est utilisée. Ci-dessous, nous montrons comment utiliser des objets de type TransformationMatrix pour transmettre les coordonnées de sortie des glyphes aux méthodes de la famille RenderGlyph().

Ainsi, pour dessiner un glyphe, nous devons créer un objet de type GlyphOutlineRenderer. Mais un tel objet ne peut pas dessiner à lui seul un glyphe. Il nécessite une fonctionnalité externe, qui est décrite par l’interface IGlyphOutlinePainter. Pour utiliser l’objet GlyphOutlineRenderer, il faut fournir une implémentation de IGlyphOutlinePainter.

Ci-dessous vous pouvez voir une implémentation simple de cette interface. Créons la classe GlyphOutlinePainter qui nécessite un objet de type System.Drawing.Drawing2D.GraphicsPath pour les objectifs de dessin graphique.

L’implémentation de l’interface figure ci-dessous. Ajoutez les surcharges de CustomDrawText() et la méthode FontWidthToImageWidth() présentées ensuite à la classe GlyphOutlinePainter. Le projet d’exemples place ces membres dans une seconde déclaration partielle de cette même classe.

 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}

Après l’implémentation de IGlyphOutlinePainter, nous pouvons utiliser des objets de ce type pour restituer un glyphe individuel en les passant au GlyphOutlineRenderer et en appelant les méthodes surchargées correspondantes de RenderGlyph() famille.

Afin de restituer une chaîne de texte, nous devons obtenir le glyphId pour chaque caractère puis, à l’aide d’un objet de type GlyphOutlineRenderer, appeler l’une des méthodes de la famille RenderGlyph(), en passant les coordonnées du glyphe correspondant.

Jetons un coup d’œil à un exemple de sortie d’une ligne de texte à l’aide de la bibliothèque Aspose.Font. Il sera construit sous la forme de la méthode CustomDrawText(), qui accepte les paramètres - CustomDrawText (texte de chaîne, police IFont, double fontSize, Brush backgroundBrush, Brush textBrush, string outFile).

Créez la méthode CustomDrawText() qui dessine le texte spécifié dans l’objet System.Drawing.Bitmap et enregistre le bitmap résultant sur le disque.

Cela comprendra les étapes suivantes :

Étapes auxiliaires pour cette stratégie

La première surcharge crée et libère une image bitmap. La seconde accepte une image appartenant au code appelant et gère l’espacement, les coordonnées, les superpositions et le rendu vertical. Ajoutez les deux surcharges à 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}

Ajoutez à cette même classe la méthode utilitaire qui convertit la largeur des unités de police en pixels :

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}

L’exemple affiche « Hello world » en Verdana noir sur blanc et en Lato bleu sur jaune. Il enregistre Custom_Verdana.png et 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);

Résultat de l’implémentation :

Rendu personnalisé du texte en Verdana Verdana

Rendu personnalisé du texte en Lato Lato

Crénage

La méthode récupère le crénage des paires dans la police. kerningCoefficient: 1.25 augmente de 25 % toute l’avance horizontale (largeur du glyphe précédent plus crénage de la paire). L’exemple utilise Verdana et enregistre 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);

Résultat de l’implémentation :

Verdana avec un coefficient d’espacement de 1.25 Verdana avec un coefficient d’espacement de 1.25

Rendu du texte par coordonnées

Positionnez le texte avec coordinateX et coordinateY. Pour obtenir la première ligne de base, la méthode ajoute fontSize * 300 / 72 pixels à coordinateY. À 18 points, coordinateX: 300 et coordinateY: 225 la placent en (300, 300). Le résultat est enregistré dans 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);

Résultat de l’implémentation :

Verdana, première ligne de base à X=300 et Y=300 Verdana, première ligne de base à X=300 et Y=300

Comment ajouter le texte à une image

Pour ajouter du texte à une image existante, transmettez une image bitmap et un pinceau de fond transparent à la méthode commune. Le pinceau transparent préserve les pixels existants.

La surcharge de CustomDrawText() acceptant une image bitmap prend déjà en charge ce scénario. Elle dessine sur l’image fournie et l’enregistre en PNG ; le code appelant reste propriétaire de l’image.

L’exemple crée OverlayBase.png dans OutputDir, puis ouvre cette image pour ajouter une ombre grise et une annotation brune. Il enregistre séparément Shadow.png et ShadowAnnotated.png ; aucune image d’entrée externe n’est nécessaire :

 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);

Résultat de l’implémentation :

Verdana avec une ombre Verdana avec une ombre

Verdana avec une ombre et une annotation. L’annotation est tronquée au bord droit de la surface de 960 pixels de large. Verdana avec une ombre et une annotation. L’annotation est tronquée au bord droit de la surface de 960 pixels de large.

Sortie du texte de haut en bas

La même surcharge permet un rendu vertical avec topDown: true. Elle place le premier glyphe sur la ligne de base initiale et décale chaque glyphe suivant de fontSize * 300 / 72 pixels vers le bas.

L’exemple affiche « Top down » en Verdana à X=400 et enregistre TopDown.png. Avec coordinateY à zéro et une taille de 18 points, la première ligne de base se trouve à Y=75 pixels :

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

Résultat de l’implémentation :

Verdana, de haut en bas Verdana, de haut en bas

Informations Complémentaires

La méthode utilise 300 DPI. scale = fontSize * dpi / 72 convertit la taille de police des points en pixels. glyphXCoordinate et glyphYCoordinate définissent la position de la ligne de base du glyphe. En mode horizontal, l’avance suivante comprend la largeur du glyphe précédent et le crénage de la paire fourni par la police ; kerningCoefficient multiplie leur somme.