TRect and TextRect Question lazarus / 39-Graphics ---------------------------------------------------------------------- Dan3468298 · 2020-07-28 00:13 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. ---------------------------------------------------------------------- wp · 2020-08-01 15:58 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  ts: TTextStyle;begin  ts := Canvas.TextStyle;  ts.Alignment := taCenter;  ts.Layout := tlCenter;  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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  ts: TTextStyle;begin  ts := Canvas.TextStyle;  ts.Alignment := taCenter;  ts.Layout := tlCenter;  Canvas.TextStyle := ts;  Canvas.TextRect(Rect(10, 10, 490, 390), 0, 0, 'My text'); ---------------------------------------------------------------------- circular · 2020-08-02 08:26 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. ---------------------------------------------------------------------- Dan3468298 · 2020-08-02 17:53 Yes, using TextOut on a Canvas.  Sorry for forgetting those details. ---------------------------------------------------------------------- asmx · 2024-01-21 04:52 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? Quote from: wp on August 01, 2020, 05:58:03 pmI 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  ts: TTextStyle;begin  ts := Canvas.TextStyle;  ts.Alignment := taCenter;  ts.Layout := tlCenter;  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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  ts: TTextStyle;begin  ts := Canvas.TextStyle;  ts.Alignment := taCenter;  ts.Layout := tlCenter;  Canvas.TextStyle := ts;  Canvas.TextRect(Rect(10, 10, 490, 390), 0, 0, 'My text'); ---------------------------------------------------------------------- circular · 2024-01-21 06:08 @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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses LCLIntf, LCLType, Types; function TextExtent(ACanvas: TCanvas; const AText: string; AMaxWidth: Integer; const AStyle: TTextStyle): TSize;var bounds: TRect;  flags: Cardinal;begin  bounds := Rect(0, 0, AMaxWidth, MaxInt);  flags := DT_CALCRECT;  if AStyle.SingleLine then flags := flags OR DT_SINGLELINE;  if AStyle.EndEllipsis then flags := flags or DT_END_ELLIPSIS;  if AStyle.ExpandTabs then flags := flags OR DT_EXPANDTABS;  if AStyle.Wordbreak then flags := flags OR DT_WORDBREAK;  if AStyle.RightToLeft then flags := flags or DT_RTLREADING;  if not AStyle.ShowPrefix then flags := flags OR DT_NOPREFIX;  LCLIntf.DrawText(ACanvas.Handle, PChar(AText), Length(AText), bounds, flags);  Result := Size(bounds.Width, bounds.Height);end;  I suppose this function could be added to TCanvas. ---------------------------------------------------------------------- asmx · 2024-01-21 08:18 @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. Quote from: circular 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses LCLIntf, LCLType, Types; function TextExtent(ACanvas: TCanvas; const AText: string; AMaxWidth: Integer; const AStyle: TTextStyle): TSize;var bounds: TRect;  flags: Cardinal;begin  bounds := Rect(0, 0, AMaxWidth, MaxInt);  flags := DT_CALCRECT;  if AStyle.SingleLine then flags := flags OR DT_SINGLELINE;  if AStyle.EndEllipsis then flags := flags or DT_END_ELLIPSIS;  if AStyle.ExpandTabs then flags := flags OR DT_EXPANDTABS;  if AStyle.Wordbreak then flags := flags OR DT_WORDBREAK;  if AStyle.RightToLeft then flags := flags or DT_RTLREADING;  if not AStyle.ShowPrefix then flags := flags OR DT_NOPREFIX;  LCLIntf.DrawText(ACanvas.Handle, PChar(AText), Length(AText), bounds, flags);  Result := Size(bounds.Width, bounds.Height);end;  I suppose this function could be added to TCanvas. ---------------------------------------------------------------------- wp · 2024-01-21 09:54 Quote from: asmx on January 21, 2024, 09:18:17 amThank 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 · 2024-01-21 12:04 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 · 2024-01-21 16:04 Quote from: asmx 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. 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 ---------------------------------------------------------------------- wp · 2024-01-21 17:16 Quote from: asmx on January 21, 2024, 01:04:40 pmPrint 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     // Define the rectangle into which the print will go. Height can be 0, will be adjust so that the text fits in.    R := Rect(...);       // Set the flags, here only workbreak.    flags := DT_WORDBREAK;       // 1st call to DrawText for measuring (with DT_CALCRECT). Afterwards the height of R has been adjusted.    DrawText(Printer.Canvas.Handle, PChar(PRINT_TEXT), Length(PRINT_TEXT), R, flags or DT_CALCRECT);      // 2nd call to DrawRect, now without DT_CALCRECT to draw wrapped text inside R.     DrawText(Printer.Canvas.Handle, PChar(PRINT_TEXT), Length(PRINT_TEXT), R, flags);       // to verify that R is correct: draw rectangle around the text, same rectangle size as used for text drawing.    Printer.Canvas.Rectangle(R); ---------------------------------------------------------------------- asmx · 2024-01-22 12:26 @wp @circular Thanks for reply again 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 · 2024-01-22 15:35 Quote from: asmx on January 22, 2024, 01:26:55 pmthe 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 · 2024-01-22 16:39 @asmx Glad to hear it works for you now. ---------------------------------------------------------------------- asmx · 2024-01-24 15:16 Quote from: wp on January 22, 2024, 04:35:24 pmQuote from: asmx on January 22, 2024, 01:26:55 pmthe 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.