Écrire dans une zone graphique avec une fonte unicode

Dans cet article, nous verrons comment insérer du texte dans une une image à l'aide de la bibliothèque tierce freetype-go. Il s'agit d'un portage en golang du moteur de rendu de fonte FreeType. Deux cas seront présentés : une application console et une application web. Bien entendu, étant donné que nous sommes francophones, j'ai particulièrement veillé à ce que la bibliothèque gère correctement l'Unicode avec un cas de figure contenant des caractères non ASCII, voire non latin.

Installation

go get code.google.com/p/freetype-go/freetype
go install code.google.com/p/freetype-go/freetype

Exemple de code

Afin de tester à fond les possibilités de la bibliothèque (et notamment sa compatibilité avec les caractères non ASCII), je me suis basé sur un texte écrit en khmer (langue officielle du Cambodge) et la fonte KhmerOS. Le but étant de produire une image contenant un texte sur deux lignes, comme dans cet exemple :

  • Exemple d'image contenant du texte

    Ce texte a été généré par freetype-go

Dans une application console

Voici le code qui s'appuie sur la bibliothèque freetype-go pour charger la police de caractère, mais également sur la bibliothèque standard de go pour dessiner et écrire le résultat dans un fichier PNG. Les variables dpi, size et spacing servent à gérer la résolution, la taille et l'écart entre les lignes. Pour le reste (par exemple, pour centrer le texte), ce serait à vous de faire les calculs (en fonction de la taille de la zone et de l'espace – qui peut être variable en fonction de la police de caractères – pris par les caractères composant la ligne). Sachez qu'il existe des bibliothèques gérant ces calculs. Autrement dit, des bibliothèques qui la chapeautent afin de vous simplifier la vie lorsque vous créez un PDF ou un graphique.

package main

import (
  "code.google.com/p/freetype-go/freetype"
  "image"
  "image/draw"
  "image/png"
  "io/ioutil"
  "os"
  "strings"
)

func main() {

text := `Comment dire "ça va ?" en khmer ?
Réponse : ជំរាបសួរ`
var dpi float64 = 72
var size float64 = 20
var spacing float64 = 1.5

  lines := strings.Split(string(text), "\n")
  data, err := ioutil.ReadFile("KhmerOS.ttf")
  if err != nil { panic(err) }
  font, err := freetype.ParseFont(data)
  if err != nil { panic(err) }
  i := image.NewRGBA(image.Rect(0, 0, 350, 90))
  draw.Draw(i, i.Bounds(), image.White, image.ZP, draw.Src)
  c := freetype.NewContext()
  c.SetDPI(dpi)
  c.SetFontSize(size)
  c.SetDst(i)
  c.SetClip(i.Bounds())
  c.SetSrc(image.Black)
  c.SetFont(font)

  pt := freetype.Pt(10, 10+int(c.PointToFix32(size)>>8))
  for _, line := range lines {
    _, err = c.DrawString(line, pt)
    if err != nil { panic(err) }
    pt.Y += c.PointToFix32(size * spacing)
  }
    
  saveToPngFile("image-texte-khmer-freetype.png", i)
}

On utilisera une fonction qui enregistrera le résultat dans un fichier PNG (la bibliothèque standard propose également le format JPEG et GIF sans compter toutes les bibliothèques tierces gérant les autres formats de fichier).

func saveToPngFile(filePath string, i *image.RGBA) {
  f, err := os.OpenFile(filePath, os.O_CREATE | os.O_WRONLY, 0666)
  if err != nil { panic(err) }
  defer f.Close() 
  err = png.Encode(f, i)
  if err != nil { panic(err) }
}

Dans une application web

On pourrait très facilement créer un contrôleur (répondant, par exemple à l'adresse http://localhost:9999/image – voir cet article) retournant une image. Changer la fonction main comme suit :

func main() {
  http.HandleFunc("/image", imageHandler)
  http.ListenAndServe(":9999", nil)
}

Puis créer le handler HTTP imageHandler en y déplaçant le code qui était précédemment dans la fonction main. La différence est que nous n'écrirons plus dans un fichier, mais sur le flux de retour web. Il ne faut pas oublier de préciser au navigateur le type de contenu en retour de sa requête, ici une image au format PNG (on peut facilement retourner un format GIF, JPEG ou autre, il suffit de changer l'encodeur et la valeur du champ Content-type avec le MIMETYPE approprié) :

  w.Header().Set("Content-type", "image/png")
  err = png.Encode(w, i)
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
  }

Les applications sont diverses. Ce code permet en effet de gérer les cas de figure où le navigateur client ne supporterait pas la police de caractères que vous utilisez ou la gèrerait mal (les anciennes versions d'Internet Explorer ou certaines versions d'Opera Mobile par exemple). On peut aussi se servir de ce code pour écrire un captcha en déformant légèrement l'image avant de l'envoyer au navigateur. Ou tout simplement écrire dans une image "combien font 1+3 ?" et demander au visiteur (humain) de faire le calcul.


Étiquettes :   image   thirdparty   freetype   texte 
Portrait de Benjamin BALET
Benjamin BALET
Consultant APM

Retrouvez mes cooordonées

Benjamin BALET sur viadeo






Vous aimerez aussi

Les lois de la réflexion

Une traduction du blog officiel de golang expliquant le mécanisme de la réflexion en Go.   Lire »

Gobs le format natif d'échange de données en Go

Traduction d'un article du blog officiel expliquant comment échanger des données entre deux programmes golang grâce à un format natif   Lire »

Comment gérer efficacement les erreurs en golang ?

Préconisations officielles pour la gestion des erreurs dans un programme golang. Cet article complète les explications sur panic, defer et recover   Lire »

Canaux et go routines avec ou sans état

Exemples d'utilisation du type chan et des go routines stateful et stateless   Lire »

Le paquet image/draw

Traduction d'un article du blog officiel de golang, cet article explique comment utiliser le paquet image/draw de la bibliothèque standard de go   Lire »

Commentaires

Soyez le premier à commenter cet article

Publier un commentaires

Tous les commentaires sont soumis à modération. Les balises HTML sont pour la plupart autorisées. Les liens contextuels et intéressants sont en follow.

(requis)
(requis)
(requis, mais non publié)
(recommandé si vous souhaitez être publié)