Recent

Author Topic: TRect and TextRect Question  (Read 4131 times)

Dan3468298

  • Full Member
  • ***
  • Posts: 131
TRect and TextRect Question
« on: July 28, 2020, 02:13:04 am »
When defining a TRect you use cartesian plane coordinates.  But when using TextRect, it ignores TRect Top and left and uses 0 value.  My guess is this is so for when TRect is used on cartesian plane with minus values for graphing.   I am trying to understand the motivations.  Thanks for any input. 
MacOS 10.15.5/Lazarus 2.0.10 Build 2020-07-07

wp

  • Hero Member
  • *****
  • Posts: 12459
Re: TRect and TextRect Question
« Reply #1 on: August 01, 2020, 05:58:03 pm »
I don't know what you are talking about. The TextRect method of the canvas?

In this case, the usage of the coordinates depends on the settings for Layout and Alignment in the canvas' TextStyle. Please play with the attached code - I wrote it on Windows but it should work also on Mac.

The rectangle Rect passed to Canvas.TextRect(Rect, x, y, Text, TextStyle) defines the box within which the text is written.
x is used only when TextStyle.Alignment is taLeftJustify, and y is used only when TextStyle.Layout is tlTop. Their origin is the origin of the canvas on which painting occurs. This means that when you want to paint in the top/left corner of the specified Rect you must set x = Rect.Left and y = Rect.Top. So, if you set x=0 and y=0 and have Rect.Left > 0 and Rect.Top > 0 there is probably no output because of clipping (which can be turned off by setting TextRect.Clipping := false).

If you want to paint the text centered at the top of the Rect use TextStyle.Alignment := taCenter, TextStyle.Layout := tlTop, x will be ignored.

If you want to right-align the text within the Rect use TextStyle.Alignment := taRightJustify, TextStyle.Layout := tlTop. x will be ignored.

If you want to right-align the text within the Rect but move it 20 pixels below the top edge use the same TextStyle settings but set y := Rect.Top + 20;

If you want to paint the text centered both vertically and horizontally within Rect use TextStyle.Alignment := taCenter and TextStyle.Layout.tlCenter, both x and y will be ignored. Etc...

Note that TTextStyle is a record which does not have setter procedures; therefore you can assign only a complete record to Canvas.TextStyle. You must use an intermediate variable to change record elements. Do not forget to initialize this record with the current TextStyle settings:
Code: Pascal  [Select][+][-]
  1. var
  2.   ts: TTextStyle;
  3. begin
  4.   ts := Canvas.TextStyle;
  5.   ts.Alignment := taCenter;
  6.   ts.Layout := tlCenter;
  7.   Canvas.TextRect(Rect(10, 10, 490, 390), 0, 0, 'My text', ts);
The temporary TextStyle variable can be omitted in the latter call, but then the temp variable must be re-assigned to canvas.TextStyle:
Code: Pascal  [Select][+][-]
  1. var
  2.   ts: TTextStyle;
  3. begin
  4.   ts := Canvas.TextStyle;
  5.   ts.Alignment := taCenter;
  6.   ts.Layout := tlCenter;
  7.   Canvas.TextStyle := ts;
  8.   Canvas.TextRect(Rect(10, 10, 490, 390), 0, 0, 'My text');

circular

  • Hero Member
  • *****
  • Posts: 4356
    • Personal webpage
Re: TRect and TextRect Question
« Reply #2 on: August 02, 2020, 10:26:50 am »
The (x,y) coordinates are redundant in most cases. TextRect could simply use (Left,Top) of rectangle.

Though I can see a case where it makes sense. If you enable clipping and disable word wrap, you can draw some text at location (x,y) and clip it according to any rectangle.
Conscience is the debugger of the mind

Dan3468298

  • Full Member
  • ***
  • Posts: 131
Re: TRect and TextRect Question
« Reply #3 on: August 02, 2020, 07:53:55 pm »
Yes, using TextOut on a Canvas.  Sorry for forgetting those details.   
MacOS 10.15.5/Lazarus 2.0.10 Build 2020-07-07

asmx

  • New Member
  • *
  • Posts: 14
Re: TRect and TextRect Question
« Reply #4 on: January 21, 2024, 05:52:41 am »
I use TextExtent to measure the width and height of a single line of text, reduce its width and then pass it into TextRect. The printing effect is that the text is wrapped. But I cannot get the actual height occupied by the printed text. Is there a good way to get it?

I don't know what you are talking about. The TextRect method of the canvas?

In this case, the usage of the coordinates depends on the settings for Layout and Alignment in the canvas' TextStyle. Please play with the attached code - I wrote it on Windows but it should work also on Mac.

The rectangle Rect passed to Canvas.TextRect(Rect, x, y, Text, TextStyle) defines the box within which the text is written.
x is used only when TextStyle.Alignment is taLeftJustify, and y is used only when TextStyle.Layout is tlTop. Their origin is the origin of the canvas on which painting occurs. This means that when you want to paint in the top/left corner of the specified Rect you must set x = Rect.Left and y = Rect.Top. So, if you set x=0 and y=0 and have Rect.Left > 0 and Rect.Top > 0 there is probably no output because of clipping (which can be turned off by setting TextRect.Clipping := false).

If you want to paint the text centered at the top of the Rect use TextStyle.Alignment := taCenter, TextStyle.Layout := tlTop, x will be ignored.

If you want to right-align the text within the Rect use TextStyle.Alignment := taRightJustify, TextStyle.Layout := tlTop. x will be ignored.

If you want to right-align the text within the Rect but move it 20 pixels below the top edge use the same TextStyle settings but set y := Rect.Top + 20;

If you want to paint the text centered both vertically and horizontally within Rect use TextStyle.Alignment := taCenter and TextStyle.Layout.tlCenter, both x and y will be ignored. Etc...

Note that TTextStyle is a record which does not have setter procedures; therefore you can assign only a complete record to Canvas.TextStyle. You must use an intermediate variable to change record elements. Do not forget to initialize this record with the current TextStyle settings:
Code: Pascal  [Select][+][-]
  1. var
  2.   ts: TTextStyle;
  3. begin
  4.   ts := Canvas.TextStyle;
  5.   ts.Alignment := taCenter;
  6.   ts.Layout := tlCenter;
  7.   Canvas.TextRect(Rect(10, 10, 490, 390), 0, 0, 'My text', ts);
The temporary TextStyle variable can be omitted in the latter call, but then the temp variable must be re-assigned to canvas.TextStyle:
Code: Pascal  [Select][+][-]
  1. var
  2.   ts: TTextStyle;
  3. begin
  4.   ts := Canvas.TextStyle;
  5.   ts.Alignment := taCenter;
  6.   ts.Layout := tlCenter;
  7.   Canvas.TextStyle := ts;
  8.   Canvas.TextRect(Rect(10, 10, 490, 390), 0, 0, 'My text');

circular

  • Hero Member
  • *****
  • Posts: 4356
    • Personal webpage
Re: TRect and TextRect Question
« Reply #5 on: January 21, 2024, 07:08:33 am »
@asmx

After investigating a bit, the best thing I found is to call LCLIntf directly.

Here is a function that you will do that for you:

Code: Pascal  [Select][+][-]
  1. uses LCLIntf, LCLType, Types;
  2.  
  3. function TextExtent(ACanvas: TCanvas; const AText: string; AMaxWidth: Integer; const AStyle: TTextStyle): TSize;
  4. var bounds: TRect;
  5.   flags: Cardinal;
  6. begin
  7.   bounds := Rect(0, 0, AMaxWidth, MaxInt);
  8.   flags := DT_CALCRECT;
  9.   if AStyle.SingleLine then flags := flags OR DT_SINGLELINE;
  10.   if AStyle.EndEllipsis then flags := flags or DT_END_ELLIPSIS;
  11.   if AStyle.ExpandTabs then flags := flags OR DT_EXPANDTABS;
  12.   if AStyle.Wordbreak then flags := flags OR DT_WORDBREAK;
  13.   if AStyle.RightToLeft then flags := flags or DT_RTLREADING;
  14.   if not AStyle.ShowPrefix then flags := flags OR DT_NOPREFIX;
  15.   LCLIntf.DrawText(ACanvas.Handle, PChar(AText), Length(AText), bounds, flags);
  16.   Result := Size(bounds.Width, bounds.Height);
  17. end;
  18.  

I suppose this function could be added to TCanvas.
Conscience is the debugger of the mind

asmx

  • New Member
  • *
  • Posts: 14
Re: TRect and TextRect Question
« Reply #6 on: January 21, 2024, 09:18:17 am »
@circular

Thank you for reply. I'm using Printer4Lazarus in my code and LCLIntf.DrawText seem can't to draw content onto the Printer.Canvas.Handle.

@asmx

After investigating a bit, the best thing I found is to call LCLIntf directly.

Here is a function that you will do that for you:

Code: Pascal  [Select][+][-]
  1. uses LCLIntf, LCLType, Types;
  2.  
  3. function TextExtent(ACanvas: TCanvas; const AText: string; AMaxWidth: Integer; const AStyle: TTextStyle): TSize;
  4. var bounds: TRect;
  5.   flags: Cardinal;
  6. begin
  7.   bounds := Rect(0, 0, AMaxWidth, MaxInt);
  8.   flags := DT_CALCRECT;
  9.   if AStyle.SingleLine then flags := flags OR DT_SINGLELINE;
  10.   if AStyle.EndEllipsis then flags := flags or DT_END_ELLIPSIS;
  11.   if AStyle.ExpandTabs then flags := flags OR DT_EXPANDTABS;
  12.   if AStyle.Wordbreak then flags := flags OR DT_WORDBREAK;
  13.   if AStyle.RightToLeft then flags := flags or DT_RTLREADING;
  14.   if not AStyle.ShowPrefix then flags := flags OR DT_NOPREFIX;
  15.   LCLIntf.DrawText(ACanvas.Handle, PChar(AText), Length(AText), bounds, flags);
  16.   Result := Size(bounds.Width, bounds.Height);
  17. end;
  18.  

I suppose this function could be added to TCanvas.

wp

  • Hero Member
  • *****
  • Posts: 12459
Re: TRect and TextRect Question
« Reply #7 on: January 21, 2024, 10:54:38 am »
Thank you for reply. I'm using Printer4Lazarus in my code and LCLIntf.DrawText seem can't to draw content onto the Printer.Canvas.Handle.
What is happening? It should work - see attachment. Sometimes (strange, not always...) the unit OSPrinters must be included in the uses clause.

asmx

  • New Member
  • *
  • Posts: 14
Re: TRect and TextRect Question
« Reply #8 on: January 21, 2024, 01:04:40 pm »
My project type is library. I also uses Interfaces, Classes, SysUtils, Graphics, LCLIntf, LCLType, Types, Printers, osprinters, etc...

Print result is nothing printed(bounds is Rect(0,0, Printer.PaperSize.Width, Printer.PaperSize.Height) and flags is DT_CALCRECT or DT_EXPANDTABS or DT_WORDBREAK or DT_NOPREFIX).

circular

  • Hero Member
  • *****
  • Posts: 4356
    • Personal webpage
Re: TRect and TextRect Question
« Reply #9 on: January 21, 2024, 05:04:47 pm »
@circular

Thank you for reply. I'm using Printer4Lazarus in my code and LCLIntf.DrawText seem can't to draw content onto the Printer.Canvas.Handle.
Hi @asmx,

That would be a different issue. The function I've provided is not designed to draw but to measure the text.

When you draw text, you can use Canvas.TextRect. This doesn't work for you?

Otherwise you could adapt the procedure I've provided by removing the DT_CALCRECT flag. If you do so, don't forget to provide coordinates (when measuring it is assumed to be at the origin 0,0).

Regards
Conscience is the debugger of the mind

wp

  • Hero Member
  • *****
  • Posts: 12459
Re: TRect and TextRect Question
« Reply #10 on: January 21, 2024, 06:16:26 pm »
Print result is nothing printed(bounds is Rect(0,0, Printer.PaperSize.Width, Printer.PaperSize.Height) and flags is DT_CALCRECT or DT_EXPANDTABS or DT_WORDBREAK or DT_NOPREFIX).
circular is right: DrawText has two purposes, depending on whether the flag DT_CALCRECT is included or not. When it is included in the flags passed to the DrawText function, then nothing is drawn, only the size of the rectangle is measured and returned in the rect parameter (where the width of the rect is not changed, only the height). So, if you want to draw, you must remove the DT_CALCRECT flag. See the code in my previous project:
Code: Pascal  [Select][+][-]
  1.     // Define the rectangle into which the print will go. Height can be 0, will be adjust so that the text fits in.
  2.     R := Rect(...);  
  3.  
  4.     // Set the flags, here only workbreak.
  5.     flags := DT_WORDBREAK;  
  6.  
  7.     // 1st call to DrawText for measuring (with DT_CALCRECT). Afterwards the height of R has been adjusted.
  8.     DrawText(Printer.Canvas.Handle, PChar(PRINT_TEXT), Length(PRINT_TEXT), R, flags or DT_CALCRECT);
  9.  
  10.     // 2nd call to DrawRect, now without DT_CALCRECT to draw wrapped text inside R.
  11.     DrawText(Printer.Canvas.Handle, PChar(PRINT_TEXT), Length(PRINT_TEXT), R, flags);  
  12.  
  13.     // to verify that R is correct: draw rectangle around the text, same rectangle size as used for text drawing.
  14.     Printer.Canvas.Rectangle(R);  

asmx

  • New Member
  • *
  • Posts: 14
Re: TRect and TextRect Question
« Reply #11 on: January 22, 2024, 01:26:55 pm »
@wp @circular

Thanks for reply again :D

This solved my problem, but I found a new problem, the alignment properties don't seem to take effect (DT_LEFT, DT_CENTER, DT_RIGHT), but I found a workaround where I manually reset the Right value after measuring the bounds before actually Print them.

wp

  • Hero Member
  • *****
  • Posts: 12459
Re: TRect and TextRect Question
« Reply #12 on: January 22, 2024, 04:35:24 pm »
the alignment properties don't seem to take effect (DT_LEFT, DT_CENTER, DT_RIGHT), but I found a workaround where I manually reset the Right value after measuring the bounds before actually Print them.
For measuring? The alignment should not play a role here, the only thing which counts is the width of the longest line and the number of lines (after wrapping).
For drawing? I don't understand then - it works here, see attached modification of the previously sent project.

circular

  • Hero Member
  • *****
  • Posts: 4356
    • Personal webpage
Re: TRect and TextRect Question
« Reply #13 on: January 22, 2024, 05:39:45 pm »
@asmx Glad to hear it works for you now.  :)
Conscience is the debugger of the mind

asmx

  • New Member
  • *
  • Posts: 14
Re: TRect and TextRect Question
« Reply #14 on: January 24, 2024, 04:16:19 pm »
the alignment properties don't seem to take effect (DT_LEFT, DT_CENTER, DT_RIGHT), but I found a workaround where I manually reset the Right value after measuring the bounds before actually Print them.
For measuring? The alignment should not play a role here, the only thing which counts is the width of the longest line and the number of lines (after wrapping).
For drawing? I don't understand then - it works here, see attached modification of the previously sent project.

@wp
Sorry, My English skills are not good enough, Some aspects may not be described clearly enough, and Thanks again :) There are currently no other issues.

I found the problem, when the text to be printed is short and not long enough to wrap, it cannot be aligned properly.
« Last Edit: January 24, 2024, 04:18:19 pm by asmx »

 

TinyPortal © 2005-2018