Search code examples
delphigditextout

How to align oriented text vertically or horizontally?


I am writing a function that draws text on a canvas. The function supports alignment vertical and horizontal, and also text orientation. My problem is that can't calculate proper alignment when text is oriented. This is the header:

procedure drawText(canvas: TCanvas; pos: TPoint; Text: string;
  FontName: TFontName; FontSize: integer; FontColor: TColor; Angle: integer;
  Halign: THorizontalAlignement; Valign: TVerticalAlignement);

Halign can be left, right or center, Valign can be top, bottom or center.

Everything works well for a simple non oriented text with:

h := TextWidth(Text);
case Halign of
    haLeft: // do nothing;
    ;
    haRight: x := x - h;
    haCenter: x := x - ( h div 2 );
  end;

  v := TextHeight(Text);
  case Valign of
    vaTop:  // do nothing;
      ;
    vaBottom: y := y - v;
    vaCenter: y := y - ( v div 2 );
  end;
  Font.Orientation := Angle;
  textOut(x, y, Text );

I have made many attempts to determine what goes where, and I have managed to position vertical text according to its alignment parameters, but the horizontal one was misplaced.

I know it is related to orientation and width and height, but i cannot figure out properly how to deal with it.

Example when calling the procedure for the horitontal rule:

    drawText( bmp.canvas, point( x, viewOption.padding - DocumentRuleTextMargin), 
inttoStr( x ), 'arial', 8, clBLack, 0, haCenter, vaBottom ); 

Calling the procedure for the Vertical rule (the one who is annoying): drawText( bmp.canvas, Point( x - CDocumentRuleTextMargin, y ), inttostr( y ), 'arial', 8, clBlack, 900, haCenter, vaBottom);

here is the result :

Example 1

i tried to get rid of this by modifying the signs in the calculation of the y position of the procedure like this :

 v := TextHeight(Text);
  case Valign of
    vaTop:  // do nothing;
      ;
    vaBottom: y := y + v;
    vaCenter: y := y + ( v div 2 );
  end;  

and the result is better for vertical rule, while worst for horizontal one :

example 2


Solution

  • OK - simple did not work. What you need to do, then, is find where the centre of your text should be and calculate 'Top left' from there after rotation. The problem is that I don't know which point the font orientates around - I guess top left. Assuming so then your function becomes:

    // get centre
    case Halign of
        haLeft: x1 := x + (h div 2);
        haRight: x1 := x - (h div 2);
        haCenter: x1 := x; // do nothing
      end;
    
      v := TextHeight(Text);
      case Valign of
        vaTop:  y1 := y + (v div 2);
        vaBottom: y1 := y - (v div 2);
        vaCenter: y1 := y; // do nothing
      end;
      Font.Orientation := Angle;
      // calculate new top left - depending on whether you are using firemonkey
      // or VCL you may need to convert to floats and/or use Cosint
      // x := x1 - (w/2)*CosD(Angle) - (h/2)*SinD(Angle);
      x := x1 - ((w * CosInt(Angle * 10)) - (h*SinInt(Angle*10)) div 2000);
      //y := y1 - (w/2)*SinD(Angle) + (h/2)*CosD(Angle);
      y := y1 - ((w * SinInt(Angle * 10)) - (h*CosInt(Angle*10)) div 2000);
      textOut(x, y, Text );
    

    Since you use Div in your code I am guessing that you are using VCL.

    I suggest that you look up SinInt for an explanation of the multiplication and division in this. the comments show the floating point version that you would use in Firemonkey.

    I have not tested this code - I am just trying to show the maths. You will need to fine tune yourself.