Ausgewählte Artikel zur Programmierung mit Delphi

Werner Voigt

 für 


Immer schön ausgerichtet

Autor: Dipl.-Math. Werner Voigt

veröffentlicht in Toolbox 5/2002, Seite 100
Copyright 2002 © Werner Voigt und Toolbox
Bitte beachten Sie auch die Hinweise zur kommerziellen Nutzung

Problem:

Wie richtet man bei der Textausgabe mit TCanvas.TextOut den Text zu anderen GDI-Objekten passend aus?

Lösung:

Bei der Textausgabe mit TCanvas.TextOut gibt es verschiedene Möglichkeiten, eine zu anderen GDI-Objekten passende Ausrichtung festzulegen. Ausgangspunkt ist stets der Platzbedarf für den Text und ein Referenzpunkt als Anker für die nötigen Rechnungen.

Der Platzbedarf ist natürlich von der gewählten Schriftart und -größe abhängig. Über GetTextExtent oder Textwidth und TextHeight werden Breite und Höhe eines Rechtecks ermittelt, in das der Text paßt. Dabei ist es belanglos, ob es sich um eine Proportionalschrift oder um eine Schrift mit festem Zeichenabstand handelt. Nun kann man, wie allgemein üblich, dieses Rechteck so platzieren, daß der Referenzpunkt auf einer Ecke, einer Seitenmitte oder in der Mitte des Rechtecks liegt. Das funktioniert auch ohne Probleme und nicht nur für Text, sondern für alle grafischen Objekte, die sich durch ein Rechteck beschreiben lassen.

In meinem Beispiel geht es alternativ um einen String oder ein Bitmap. Über zwei Radiogroups (mit recht wenig Text) wird die Lage des Rechtecks zum Referenzpunkt bestimmt. Mit der Checkbox wird zwischen String und Bitmap gewechselt. So weit, so gut. Aber damit lassen sich nicht alle Ausrichtungsanforderungen an Text erfüllen. Dazu brauchen wir nur daran zu denken, unterschiedlich hohe Schriften in einer Zeile zu kombinieren. Dann ist der Wunsch naheliegend, alle Zeichen mögen auf der gleichen Linie stehen. Diese Linie heißt bei Microsoft treffend Baseline (Grundlinie). Mit der Windows-API-Funktion GetTextMetrics kann man u.a. ermitteln, wie viel Pixel es von der Zellenoberkante bis zur Grundlinie sind.

Ohne langes Nachdenken habe ich bisher akzeptiert, das die Textausgabe immer bezüglich der linken oberen Ecke dieses Rechtecks erfolgt. Dabei gibt es eine einfache Möglichkeit, das Verhalten von TextOut zu beeinflussen. Die Windows-API-Funktion SetTextAlign bestimmt bis zu ihrem nächsten Aufruf wie der Text durch das GDI ausgerichtet wird. Damit kann man die gleichen Ausrichtungseffekte wie oben beschrieben erreichen. Mit zwei Unterschieden:

  1. um die Rechnerei kümmert sich Windows, wir rufen nur zusätzlich SetTextAlign auf
  2. in der Senkrechten gibt es anstelle des Zentrierens jetzt die Ausrichtung auf der Grundlinie

Im zweiten Teil meines Beispiels werden diese Möglichkeiten demonstriert. Dort wird ein kurzer Text in allen neun Ausrichtungsvarianten, die SetTextAlign bietet, in eine PaintBox gezeichnet.

procedure TAlignForm.FormPaint(Sender: TObject);
begin
{ ergänzt nur das Standardpaint um die Darstellung der
  Ausrichtungsbox }
  alClick(nil);
end;

{ 1. Demo der Ausrichtungsvarianten durch Platzbedarfsberechnung }
procedure TAlignForm.alClick(Sender: TObject);
// diese Methode bedient die OnClick-Events der GroupBoxen
var
  tx: string;
  x, y,
  w, h: integer;
  R: TRect;
begin
  with AlignForm.Canvas
  do begin
  { Text oder Bitmap ausgerichtet darstellen }
    SetTextAlign(AlignForm.Canvas.Handle, TA_LEFT+TA_TOP);
    Brush.Color := clWhite;
    Brush.Style := bsSolid;
    Pen.Color := clGreen;
    // Panel liefert nur ein Rechteck
    x := Panel.Left;
    y := Panel.Top;
    // 1. Hintergrund löschen
    FillRect(Rect(x, y, Panel.Left+Panel.Width,
                        Panel.Top+Panel.Height));
    // 2. Mitte = Referenzpunkt ermitteln
    x := x + Panel.Width div 2;
    y := y + Panel.Height div 2;
    // 3. Kreuz malen
    MoveTo(x, Panel.Top);
    LineTo(x, Panel.Top+Panel.Height);
    MoveTo(Panel.Left, y);
    LineTo(Panel.Left+Panel.Width, y);
    Brush.Style := bsClear;
    Font.Name := 'MS Sans Serif';
    Font.Size := 16;
    Font.Color := clRed;
    Font.Style := [];
    { Erklärung:    Referenzpunkt  Verschiebung
      ItemIndex 0 - Right/Bottom   volle Breite, Höhe weg
                1 - Center/Center  halbe Breite, Höhe weg
                2 - Left/Top       x, y bleiben so
    }
    if (Logo<>nil) and LogoText.Checked
    then begin
// das Logo war mir zu groß, deshalb div 4 und StretchDraw
      w := Logo.Width div 4;
      h := Logo.Height div 4;
      R := Rect(0, 0, w, h);
      OffSetRect(R, x-w+(alX.ItemIndex*w div 2),
                    y-h+(alY.ItemIndex*h div 2));
      StretchDraw(R, Logo);
    end
    else begin
// Textausgabe
      tx := 'Toolbox';
      w := TextWidth(tx);
      h := TextHeight(tx);
      TextOut(x-w+(alX.ItemIndex*w div 2),
              y-h+(alY.ItemIndex*h div 2), tx);
    end;
  end;
end;

procedure TAlignForm.LogoTextClick(Sender: TObject);
begin
  { Umschaltung Bitmap/Text, deshalb Neudarstellung }
  alClick(nil);
end;

procedure TAlignForm.TextChange(Sender: TObject);
begin
  PaintBox1.Invalidate;
end;

procedure TAlignForm.PaintBox1Paint(Sender: TObject);
var
  DC: hDC; { für SetTextAlign }
  i, j: integer;
  x, y: integer;
const
// Beschriftungstexte
  AlignX: array[0..2] of string=('Left', 'Center', 'Right');
  AlignY: array[0..2] of string=('Top', 'Base', 'Bottom');
// Parameterwerte für SetTextAlign
  alValX: array[0..2] of integer = (TA_LEFT, TA_CENTER,
                                    TA_RIGHT);
  alValY: array[0..2] of integer = (TA_TOP, TA_BASELINE,
                                    TA_BOTTOM);
begin
  DC := PaintBox1.Canvas.Handle;
  SetTextAlign(DC,TA_LEFT+TA_BASELINE);
  with PaintBox1.Canvas
  do begin
    x := 80;
    y := 40;
    Pen.Color := clBlack;
    Font.Name := 'Arial';
    Font.Color := clBlack;
    Font.Size := 12;
    Font.Style := [fsBold];
    Brush.Color := clWhite;
    FillRect(PaintBox1.ClientRect);
    SetTextAlign(DC, TA_CENTER+TA_BASELINE);
    Brush.Style := bsClear;
// waagerechte Bezeichnung
    for j:=0 to 2
    do TextOut(x+j*100, y-20, AlignX[j]);

    for i:=0 to 2
    do begin
// senkrechte Bezeichnung
      SetTextAlign(DC, TA_LEFT+TA_TOP);
      TextOut(x-70, y+i*50-(TextHeight(AlignY[i]) div 2),
              AlignY[i]);
      for j:=0 to 2
      do begin
        Pen.Color := clRed;
// Referenzpunkt
        MoveTo(x+j*100-10,y+i*50);
        LineTo(x+j*100+10,y+i*50);
        MoveTo(x+j*100,y+i*50-10);
        LineTo(x+j*100,y+i*50+10);
        Ellipse(x+j*100-2,y+i*50-2,x+j*100+3,y+i*50+3);
// Ausrichtung festlegen
        SetTextAlign(DC, alValX[j]+alValY[i]);
        Pen.Color := clBlue;
// Text aus Edit-Feld
        TextOut(x+j*100, y+i*50, Text.Text);
      end;
    end;
    Font.Size := 12;
    Font.Style := [];
  end;
end;
    
zurückzurück
Copyright © 2003 by  EDVoigt  (Werner Voigt).