2010-01-25 16 views
5

Mon problème:

Je travaille actuellement sur un contrôle utilisateur personnalisé qui affiche des morceaux de texte (chacun avec une police potentiellement différente) sur une ligne. Je voudrais aligner tous ces bits de texte exactement le long d'une ligne de base commune. Par exemple:En utilisant GDI +, quelle est l'approche la plus simple pour aligner du texte (dessiné dans plusieurs polices différentes) le long d'une ligne de base commune?

Hello, I am George. 
------------------------------ <- all text aligns to a common baseline 
    ^  ^ ^
    |   |  | 
Courier Arial Times  <- font used for a particular bit of text 
    20pt  40pt  30pt 

Parce que je ne l'ai pas trouvé de GDI + fonctionnalités pour le faire directement, je suis venu avec ma propre méthode (décrite ci-dessous). Cependant:

Je me demande s'il n'y a vraiment pas un moyen plus facile d'y arriver?

Mon approche actuelle:

1) Gather une liste de tous les System.Drawing.Font s qui seront utilisés pour l'affichage de texte.

2) Pour chaque Font, pour la position verticale de la ligne de base en pixels, en utilisant le code suivant:

// variables used in code sample (already set) 
Graphics G; 
Font font; 
... 

// compute ratio in order to convert from font design units to pixels: 
var designUnitsPerPixel = font.GetHeight(G)/
          font.FontFamily.GetLineSpacing(font.Style); 

// get the cell ascent (baseline) position in design units: 
var cellAscentInDesignUnits = font.FontFamily.GetCellAscent(font.Style); 

// finally, convert the baseline position to pixels: 
var baseLineInPixels = cellAscentInDesignUnits * designUnitsPerPixel; 

3) Pour tous les Font s utilisés, déterminer la valeur baseLineInPixels maximale calculé ci-dessus et stocker cette valeur à maxBaseLineInPixels.

4) Dessiner chaque bit de texte de la manière suivante:

// variables used in code sample (already set): 
Graphics G; 
Font font; 
string text; 
... 

// find out how much space is needed for drawing the text 
var measureF = G.MeasureString(text, font); 

// determine location where text will be drawn: 
var layoutRectF = new RectangleF(new PointF(0f, 0f), measureF); 
layoutRectF.Y += maxBaseLineInPixels - baseLineInPixels; 
//^the latter value 'baseLineInPixels' is specific to the font used 

// draw text at specified location 
G.DrawString(text, font, Brushed.Black, layoutRectF); 

Suis-je manque quelque chose, ou est-il vraiment aucun moyen plus facile?

+1

Sachez-le aujourd'hui et hier. Semble être la seule solution. Notez que Font.GetHeight (Graphics) retournera la valeur dans le PageUnit de l'objet Graphics, plutôt que dans les pixels. J'utilise maintenant la solution de Bob Powell (http://www.bobpowell.net/formattingtext.htm) maintenant, en utilisant le dernier bloc de code dans son article (bien, en C#), et en convertissant en millimètres, puisque j'en ai besoin pour une routine de création partagée d'impression/dessin/PDF. – OregonGhost

+0

Merci pour vos commentaires. Je suis soulagé d'entendre (eh bien ... :) que dessiner du texte avec GDI + en fait * est * si compliqué et que je n'imagine pas seulement ça. J'ai depuis décidé de laisser tomber ce problème et de continuer vers WPF (où GDI + ne compte plus et les choses seront heureusement un peu plus faciles). – stakx

Répondre

0

Je pense que cette façon est le travail, s'il vous plaît essayez.

List<RectangleF> rects = new List<RectangleF>(); 

private void Form1_Paint(object sender, PaintEventArgs e) 
{ 
    ////////////////////Not Set baseLine 
    //baseline 
    e.Graphics.DrawLine(Pens.Red , new Point(100,200),new Point(800,200)); 

    //words 
    Point point = new Point(100,100); 
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, point); 
    RectangleF rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30))); 
    e.Graphics.DrawRectangle(Pens.Green,rectangleF.X ,rectangleF.Y , rectangleF.Width , rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(400, 100); 
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, point); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40))); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(800, 100); 
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, point); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20))); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    ///////////////////SetBaseLine///////////////////////////// 
    var maxHeight = GetMaxHeight(); 
    /////////////////// 

    //baseLine 
    e.Graphics.DrawLine(Pens.Pink, new Point(100, (int) (400 + maxHeight/2)), new Point(800, (int) (400 + maxHeight/2))); 

    StringFormat stringFormat = new StringFormat(); 
    stringFormat.LineAlignment = StringAlignment.Center; 

    //words 
    point = new Point(100, 400); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30))); 
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, new RectangleF(rectangleF.X ,rectangleF.Y , rectangleF.Width , maxHeight) , stringFormat); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(400, 400); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40))); 
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(800, 400); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20))); 
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

} 

private float GetMaxHeight() 
{ 
    float temp = 0; 
    foreach (RectangleF rectangleF in rects) 
     if (rectangleF.Height > temp) 
      temp = rectangleF.Height; 

    return temp; 
} 
+0

Merci de votre publication. Cependant, cette solution n'est malheureusement pas utile ... désolé. C'est pour deux raisons: ** 1. ** J'ai depuis longtemps abandonné à ce sujet (voir la date de publication originale) et a décidé de passer à WPF à la place. ** 2. ** Je pense qu'il y a de bonnes approches dans votre code ('MeasureString',' GetMaxHeight'); malheureusement, la plupart du code restant suggère autre chose, à savoir que vous travaillez uniquement avec des coordonnées fixes pré-calculées et un texte de dessin deux fois (ce qui semble inutile). Néanmoins, merci d'avoir pris le temps de répondre. – stakx

0

J'ai fait des recherches la même chose pour les derniers jours, et j'ai finalement trouvé une réponse on this blog page. Ce code (au bas de l'article) a vraiment bien fonctionné pour moi et j'espère aider quelqu'un d'autre luttant avec ce problème:

private void DrawOnBaseline(string s, Graphics g, Font f, Brush b, Point pos) 
    { 
     float baselineOffset=f.SizeInPoints/f.FontFamily.GetEmHeight(f.Style)*f.FontFamily.GetCellAscent(f.Style); 
     float baselineOffsetPixels = g.DpiY/72f*baselineOffset; 

     g.DrawString(s,f,b,new Point(pos.X,pos.Y-(int)(baselineOffsetPixels+0.5f)),StringFormat.GenericTypographic); 
    }