Board index » delphi » Fix for TCanvas.TextWidth, TextHeight bugs ?

Fix for TCanvas.TextWidth, TextHeight bugs ?

Delphi 4 Pro, update pack 3.

TCanvas.TextHeight and TCanvas.TextWidth each have at
least two bugs in them
1.)  Best shown by example:
       S := 'Line1'+#13+'Another Line'+#13+'A Third Line';
TCanvas.TextWidth(S)    should return the same value as for
TCanvas.TextWidth('Another Line'), but instead returns a
value equivalent to TextWidth('Line1Another LineA Third Line');

Similarly, TextHeight(S) should return a value about three times
the height of any of the individual lines within S, but in reality
the height of 'Line1Another LineA Third Line' is returned.

2.)  Bolded fonts require more space than regular fonts, but
TextWidth and TextHeight do not take this into account.   They
ignore the actual font style and assume the font style is "regular".

I've looked everywhere at Borland and on many non-Borland Delphi
sites for a fix for this with no luck.

With the old Borland Pascal, I had no hesitation about changing the
source code in the RTL to fix annoying little bugs like this, but with
Delphi I am reluctant to try to fix TCanvas.TextHeight and TextWidth
simply because too darn many things use a TCanvas.   I'm worried
that by fixing one bug I'll cause a chain reaction that will keep me
busy fixing new bugs for years.

Rob

 

Re:Fix for TCanvas.TextWidth, TextHeight bugs ?


Rob Stow schrieb:

Quote
> TCanvas.TextHeight and TCanvas.TextWidth each have at
> least two bugs in them
> 1.)  Best shown by example:
>        S := 'Line1'+#13+'Another Line'+#13+'A Third Line';
> TCanvas.TextWidth(S)    should return the same value as for
> TCanvas.TextWidth('Another Line'), but instead returns a
> value equivalent to TextWidth('Line1Another LineA Third Line');

> Similarly, TextHeight(S) should return a value about three times
> the height of any of the individual lines within S, but in reality
> the height of 'Line1Another LineA Third Line' is returned.

TCanvas.TextWidth and .TextHeight both call TCanvas.TextExtent, which
does an API call to GetTextExtentPoint32. This Win function does not
take line breaks into account.

Try the following function as a workaround:

function GetTextSize(ACanvas: TCanvas; const AString: String): TSize;
var
  R: TRect;
begin
  FillChar(R, SizeOf(R), 0);
  DrawText(ACanvas.Handle, PChar(AString), Length(AString), R,
    DT_CALCRECT or DT_NOPREFIX);
  Result.cx := R.Right;
  Result.cy := R.Bottom;
end;

Quote
> 2.)  Bolded fonts require more space than regular fonts, but
> TextWidth and TextHeight do not take this into account.

My one above does.

-Michael

Re:Fix for TCanvas.TextWidth, TextHeight bugs ?


Rob Stow <rob.s...@cnnsimail.com> schreef in berichtnieuws
39A75823.57CB4...@cnnsimail.com...

Quote
> Delphi 4 Pro, update pack 3.

> TCanvas.TextHeight and TCanvas.TextWidth each have at
> least two bugs in them
> 1.)  Best shown by example:
>        S := 'Line1'+#13+'Another Line'+#13+'A Third Line';
> TCanvas.TextWidth(S)    should return the same value as for
> TCanvas.TextWidth('Another Line'), but instead returns a
> value equivalent to TextWidth('Line1Another LineA Third Line');

This is not a bug. Deep down in Delphi, TextWidth() calls
GetTextExtentPoint() in the Microsoft Windows API. Control characters like
CR and LF will be shown as a | character.
If you were to do canvas.textout(x,y,s) with s as above, you would see that
the string is output as Line1|Another Line|A Third Line. So TextWidth()
gives the correct result for what the system will do if the string is drawn
on the canvas.
You cannot draw multiple lines of text by simply adding #13. You will need
to do this line by line, advancing the y position with TextHeight + some
extra pixels.

--
Regards,

Dirk Claessens
---------------------------------------------------------
Attention: All spamshields raised; E-mails will bounce!
---------------------------------------------------------

Re:Fix for TCanvas.TextWidth, TextHeight bugs ?


Quote
Dirk Claessens wrote:
> Rob Stow <rob.s...@cnnsimail.com> schreef in berichtnieuws
> 39A75823.57CB4...@cnnsimail.com...
> > Delphi 4 Pro, update pack 3.

> > TCanvas.TextHeight and TCanvas.TextWidth each have at
> > least two bugs in them
> > 1.)  Best shown by example:
> >        S := 'Line1'+#13+'Another Line'+#13+'A Third Line';
> > TCanvas.TextWidth(S)    should return the same value as for
> > TCanvas.TextWidth('Another Line'), but instead returns a
> > value equivalent to TextWidth('Line1Another LineA Third Line');

> This is not a bug. Deep down in Delphi, TextWidth() calls
> GetTextExtentPoint() in the Microsoft Windows API. Control characters like
> CR and LF will be shown as a | character.

Then the API function is buggy because it doesn't take line breaks
into account.  TextWidth and TextHeight are still buggy because
they neither implement a workaround for a bug Borland apparently
knew was in the API, nor did they mention the consequences of the
API bug to programmers so that they know that they have to create
their own components if they want something with a TextHeight and
TextWidth that work properly.

Quote
> If you were to do canvas.textout(x,y,s) with s as above, you would see that
> the string is output as Line1|Another Line|A Third Line.

 In my specific case I want a TStaticText with the minimum
width and height required to display my three line string.

If I do something like
     TStaticText.Caption := 'line 1'+#13+'line 2'+#13+'line 3'
the TStaticText  WILL take the line breaks into account when
it draws the text on its canvas.    What it won't do is properly
resize itself when the caption changes because the TextHeight
and TextWidth methods are incorrect:   it returns the width
and height of what the Caption would be if the line breaks were
omitted.

Quote
> So TextWidth()
> gives the correct result for what the system will do if the string is drawn
> on the canvas.

Not true in all cases - the TStaticText for example.
Not true in ANY case for which you have used a font style other
than "regular" since TextWidth() does NOT take into account that
bolded or italicized strings need more room.

Quote
> You cannot draw multiple lines of text by simply adding #13.

With many Delphi components you CAN.   It works with Hints,
TStaticText, TLabel and undoubtedly others.  You simply have
to take into account the bugs in TextHeight and TextWidth so
that the component will be big enough for your text.

Rob

Re:Fix for TCanvas.TextWidth, TextHeight bugs ?


Its awfully easy to blame the development tool or o/s. You seem to believe
that TextWidth & TextOut should handle multi-line text. The fact that
TextOut takes an (x,y) starting point doesn't seem to have convinced you
that it might not be appropriate for drawing more than one line of text.

Neither TextWidth, TextOut, or the API routines they use are buggy in the
way you describe - they simply were not designed and don't work the way you
think they should. If you want to measure and draw multi-line text why not
simply use the API call designed to do that (DrawText)? Isn't it a lot
simpler than blaming your tools for not doing something they were never
designed to do?

Re:Fix for TCanvas.TextWidth, TextHeight bugs ?


Rob Stow <rob.s...@cnnsimail.com> schreef in berichtnieuws
39A82824.5A534...@cnnsimail.com...

Quote
> Dirk Claessens wrote:

[snip]

Quote
> Then the API function is buggy because it doesn't take line breaks
> into account.

Rob,

 As Bruce and I tried to explain,
TextWidth() ->WIN32API.GetTextExtentPoint() do
 exactly what they were _designed_ for, i.e. : _single_ line text output on
a _canvas_.  Therefore, cr/lf/backspace/etc.. will not be taken into
account.

See what TextOut() actually does in Graphics.pas :
------------------------------------------------------------
procedure TCanvas.TextOut(X, Y: Integer; const Text: String);
begin
  Changing;
  RequiredState([csHandleValid, csFontValid, csBrushValid]);
  if CanvasOrientation = coRightToLeft then Inc(X, TextWidth(Text) + 1);
  Windows.ExtTextOut(FHandle, X, Y, FTextFlags, nil, PChar(Text),
   *****See? only string _length_ is taken into account here*****
   Length(Text), nil);
  MoveTo(X + TextWidth(Text), Y);
  Changed;
end;

[snip]

Quote
> If I do something like
>      TStaticText.Caption := 'line 1'+#13+'line 2'+#13+'line 3'
> the TStaticText  WILL take the line breaks into account when
> it draws the text on its canvas.

[snip]

TStaticText does not _have_ a canvas, that's the whole point!
TstaticText is derived from TCustomStaticText, which indeed will
take line breaks into account. Extract from StdCtrls.pas:

Adjustbounds() is called from TextChanged()
----------------------------------------
procedure TCustomStaticText.AdjustBounds;
var
  DC: HDC;
  SaveFont: HFont;
  TextSize: TSize;
begin
  if not (csReading in ComponentState) and FAutoSize then
  begin
    DC := GetDC(0);
    SaveFont := SelectObject(DC, Font.Handle);
    =========================================Both width and height here !
    GetTextExtentPoint32(DC, PChar(Caption), Length(Caption), TextSize);
    =======================================================
    SelectObject(DC, SaveFont);
    ReleaseDC(0, DC);
    SetBounds(Left, Top,
      TextSize.cx + (GetSystemMetrics(SM_CXBORDER) * 4),
      TextSize.cy + (GetSystemMetrics(SM_CYBORDER) * 4));
  end;
end;

As Bruce suggested, you might want to try DrawText(), or, if your app must
run under NT,DrawTextEx().

Always bear in mind that Borland for the development of Delphi/Builder/etc..
must comply to the WINAPI as designed by MS, whether they ( or you, or me )
like it or not.

--
Regards,

Dirk Claessens
---------------------------------------------------------
Attention: All spamshields raised; E-mails will bounce!
---------------------------------------------------------

Re:Fix for TCanvas.TextWidth, TextHeight bugs ?


Quote
"Dirk Claessens" <will.bou...@back.com> wrote in message

news:Xgz5TUDEAHA.411@btbntsys4...

Quote
> As Bruce suggested, you might want to try DrawText(), or, if your app must
> run under NT,DrawTextEx().

My help indicates that DrawText works with NT. Is this not the case? Or is
it just with some versions of NT?

Re:Fix for TCanvas.TextWidth, TextHeight bugs ?


Bruce Roberts heeft geschreven in bericht ...

Quote

>"Dirk Claessens" <will.bou...@back.com> wrote in message
>news:Xgz5TUDEAHA.411@btbntsys4...

>> As Bruce suggested, you might want to try DrawText(), or, if your app
must
>> run under NT,DrawTextEx().

>My help indicates that DrawText works with NT. Is this not the case? Or is
>it just with some versions of NT?

Uhmm...Ehrmm...  you're right. Both work for NT.
 I must have been reading too fast again.

Regards,

Dirk Claessens -  Agfa Belgium

---------------------------------------------------------------------------
ATTENTION : All spamshields are raised -- direct emails will bounce!
---------------------------------------------------------------------------

Other Threads