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:
- um die Rechnerei kümmert sich Windows, wir rufen nur zusätzlich SetTextAlign auf
- 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;
|
