1

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

LU RD
  • 34,438
  • 5
  • 88
  • 296
nico
  • 127
  • 1
  • 13
  • I'd expect you to set the angle first, and then ask for the dimensions. TextExtent gives you those. – David Heffernan Mar 08 '16 at 17:22
  • Use TextRect, first with tfCalcRect. Let GDI calculate required oriented width and heigth. – Sertac Akyuz Mar 08 '16 at 17:35
  • True. I did many tries focusing on orientation before of after querying for the text size, but it did not change at all the position. – nico Mar 08 '16 at 17:36
  • Actually, textRect is designed to clip text inside a rectangle right, i am looking for a solution to place a text where i want, not to clip it. I'm pretty sure that TextExtent, TextWidth and TextHeight functions works very well, my only problem is that they behave differently depending on the orientation and i can't guess how. – nico Mar 09 '16 at 09:27

3 Answers3

2

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.

Dsm
  • 5,870
  • 20
  • 24
0

I think that the vertical rule in your case should be

drawText( bmp.canvas, Point( x - CDocumentRuleTextMargin, y ), inttostr( y ), 'arial', 8, clBlack, 900, haCenter, vaCenter);

because you are trying to align to the checkmarks, and they need to be centred. Changing your algorithm moved the vertical positions as expected so it looks like your original algorithm is correct - just your application of it is wrong.

Dsm
  • 5,870
  • 20
  • 24
  • Actually, the calculation of alignements parameter is not well calculated, so changing it to vaCenter does not fix my issue. This is the point : I'm looking for the right way to calculate it when text is vertical or horizontal – nico Mar 09 '16 at 12:01
0

The problem is that width and height does not change while rotating the text.

When using a 90° rotation, what returns textHeight function is the actual(visible) width. Same thing for textWidth that represents the visible height.

In this case, it is not possible to center vertically and horizontally a 90° rotated text using the same formula than an horizontal text ( i.e : substracting half of the width to the x position will result in a too big displacement ).

Since i only manage vertical and horizontal text, i will use a workaround by testing the orientation property. When 900 then i switch textHeight and textwidth results to calculate the aligned position of the text.

nico
  • 127
  • 1
  • 13