alogrep 0 Posted June 24, 2023 Hi. I used the code below to print Richedit in pre-XE Delphi for many years. Now it get stuck in the repeat loop: In my test, the first nextchar gives 571, in the next and all subsequent cals gives 570. Gence the loop hangs. Does Anybody have any ideas as to what has changed or what am I doing wrong? Printer.BeginDoc; try With Printer.Canvas Do Begin printresX := GetDeviceCaps( handle, LOGPIXELSX ); printresY := GetDeviceCaps( handle, LOGPIXELSY ); printarea:= Rect( printresX div 2, // 0.5 inch left margin printresY div 2, // 0.5 inch top margin Printer.PageWidth - (printresX div printresX), // 0.5 inch right margin Printer.PageHeight - (printresY-2) // 1 inch bottom ); richedit_outputarea := Rect( (printarea.left) * 1440 div printresX, ((printarea.top) * 1440 div printresY), (printarea.right) * 1440 div printresX, (printarea.bottom)* 1440 div printresY ); fmtRange.hDC := Handle; // printer handle fmtRange.hdcTarget := Handle; // ditto fmtRange.rc := richedit_outputarea; fmtRange.rcPage := Rect( 0, 0, Printer.PageWidth * 1440 div printresX, Printer.PageHeight * 1440 div printresY ); fmtRange.chrg.cpMin := 0; fmtRange.chrg.cpMax := richedit1a.GetTextLen; // remove characters that need not be printed from end of selection. // failing to do so screws up the repeat loop below. S:= richedit1a.Text; While (fmtRange.chrg.cpMax > 0) and (S[fmtRange.chrg.cpMax] <= ' ') Do Dec(fmtRange.chrg.cpMax); pageof:=0; //total number of pages Repeat // only count pages here. // Render the text nextChar := richedit1a.Perform( EM_FORMATRANGE, 0, Longint(@fmtRange)); fmtRange.rc := richedit_outputarea; Inc(pageof); If nextchar < fmtRange.chrg.cpMax Then Begin // more text to print fmtRange.chrg.cpMin := nextChar; End; { If } Until nextchar >= fmtRange.chrg.cpMax; Share this post Link to post
PeterBelow 238 Posted June 24, 2023 (edited) First thing to do is to get rid of the with statement. That just confuses things. Be explicit, use Printer.Canvas.Handle instead of just Handle. And do you ever start a new page? I do not see a Printer.NewPage in your loop. If I look at the C example code here nextchar <= fmtRange.chrg.cpMin is possible and should be handled as an error. Edited June 24, 2023 by PeterBelow Share this post Link to post
alogrep 0 Posted June 24, 2023 Thanks Peter. I applied your suggestions (I did not have the Newpage, because this richedit text will never exceed 1 page). However, checking if nextchar <=fmtRange.chrg.cpMin allows me to abort printing but it does not tell me what is the error, why that happens. Share this post Link to post
alogrep 0 Posted June 24, 2023 I g=found this code here: https://stackoverflow.com/questions/22234371/how-to-print-the-contents-of-a-richedit. It seems to work pretty well. However, I like to print a small logo (saved as abitmap separately) at the sart of the printing, followed by the richedit content, all on the same page.. Is that possible? procedure PrintRichEdit(RichEdit: TRichEdit; const Caption: string; const PrinterMargin: Integer); //the units of TRichEdit.PageRect are pixels, units of PrinterMargin are mm var PrinterHeight, PrinterWidth: Integer; LogPixels, PrinterTopLeft: TPoint; PageRect: TRect; Handle: HDC; begin Handle := Printer.Handle; LogPixels := Point(GetDeviceCaps(Handle, LOGPIXELSX), GetDeviceCaps(Handle, LOGPIXELSY)); PrinterTopLeft := Point(GetDeviceCaps(Handle, PHYSICALOFFSETX), GetDeviceCaps(Handle, PHYSICALOFFSETY)); PrinterWidth := Printer.PageWidth; PrinterHeight := Printer.PageHeight; PageRect.Left := Max(0, Round(PrinterMargin*LogPixels.X/25.4) - PrinterTopLeft.X); PageRect.Top := Max(0, Round(PrinterMargin*LogPixels.Y/25.4) - PrinterTopLeft.Y); PageRect.Right := PrinterWidth-PageRect.Left; PageRect.Bottom := PrinterHeight-PageRect.Top; if (PageRect.Left>=PageRect.Right) or (PageRect.Top>=PageRect.Bottom) then //the margins are too big PageRect := Rect(0, 0, 0, 0); RichEdit.PageRect := PageRect; RichEdit.Print(Caption); end; Share this post Link to post
PeterBelow 238 Posted June 25, 2023 16 hours ago, alogrep said: Thanks Peter. I applied your suggestions (I did not have the Newpage, because this richedit text will never exceed 1 page). However, checking if nextchar <=fmtRange.chrg.cpMin allows me to abort printing but it does not tell me what is the error, why that happens. If your text fits into one page it may simply indicate that all text has been printed.... Share this post Link to post
PeterBelow 238 Posted June 25, 2023 16 hours ago, alogrep said: However, I like to print a small logo (saved as abitmap separately) at the sart of the printing, followed by the richedit content, all on the same page.. Is that possible? Certainly. You can mix direct output to the printer canvas with rendering via EM_FORMATRANGE. By the way: do you know Code News Fast? It is an excellent source for Delphi code examples and it even has all the content of the long defunct Delphi newsgroups on hand. This old post may be a good starting point. By the way: the change in behaviour of your printing code from XE to Delphi 11 may be due to the fact that D11 uses a different version of the richedit common control (4.x) than XE did (2.x). There are suptle differences between these control versions. Share this post Link to post
alogrep 0 Posted June 26, 2023 Thanks Peter. I copied from the TcustonRicheit.Print() founcion to modify my code (now includes printing multiple pages) as shown in the code section below. It works just fine. var ...... MaxLen: Integer; TextLen: TGetTextLengthEx; ........ try Printer.BeginDoc; TextLen.Flags := GTL_NUMCHARS; TextLen.CodePage := 1200; // Unicode MaxLen := SendMessage(richedit1a.Handle, EM_GETTEXTLENGTHEX, LPARAM(@TextLen), 0); try With Printer.Canvas Do Begin printresX := GetDeviceCaps( Printer.Canvas.handle, LOGPIXELSX ); printresY := GetDeviceCaps( Printer.Canvas.handle, LOGPIXELSY ); if reptype= 13 then printarea:=Rect(1,printResY div 2, Printer.PageWidth-1,Printer.PageHeight - (printresY-2)) else printarea:= Rect( printresX div 2, // 0.5 inch left margin printresY div 2, // 0.5 inch top margin Printer.PageWidth - (printresX div printresX), // 0.5 inch right margin Printer.PageHeight - (printresY-2) // 1 inch bottom ); richedit_outputarea := Rect( (printarea.left) * 1440 div printresX, (printarea.top * 1440 div printresY), (printarea.right) * 1440 div printresX, (printarea.bottom)* 1440 div printresY ); fmtRange.hDC := Printer.Canvas.Handle; // printer handle fmtRange.hdcTarget := Printer.Canvas.Handle; // ditto fmtRange.rc := richedit_outputarea; fmtRange.rcPage := Rect( 0, 0, Printer.PageWidth * 1440 div printresX, Printer.PageHeight * 1440 div printresY ); fmtRange.chrg.cpMin := 0; fmtRange.chrg.cpMax := richedit1a.GetTextLen; pageof:=0; //total number of pages Repeat // only count pages here. // Render the text nextChar := SendStructMessage(richedit1a.Handle, EM_FORMATRANGE, 0,fmtRange); Inc(pageof); If nextchar < MaxLen Then Begin // more text to print fmtRange.chrg.cpMin := nextChar; End; { If } Until nextchar >= Maxlen; // Free cached information richedit1a.Perform( EM_FORMATRANGE, 0, 0); fmtRange.hDC := Printer.Canvas.Handle; // printer handle fmtRange.hdcTarget := Printer.Canvas.Handle; // ditto fmtRange.rc := richedit_outputarea; fmtRange.rcPage := Rect( 0, 0, Printer.PageWidth * 1440 div printresX, Printer.PageHeight * 1440 div printresY ); fmtRange.chrg.cpMin := 0; fmtRange.chrg.cpMax := richedit1a.GetTextLen; page := 1; Repeat // now actually print // Render the text nextChar := SendStructMessage(richedit1a.Handle, EM_FORMATRANGE, 1,fmtRange); if not (reptype in [9,10,12]) then with dm1.systable do begin if reptype <> 13 then Textout(printarea.left{+printresx},printarea.bottom+ (printresy div 2),FieldByName('Name').AsString+' '+LG('Report Printed')+' '+FormatDateTime('dd-mmm-yyyy hh:mm:ss',Now)); TextOut(printarea.right-printresx,printarea.bottom+ (printresy div 2), 'Page: '+inttostr(page)+ ' ' +lg('of') +' '+IntToStr(pageof)); end; Inc(page); If nextchar < MaxLen Then Begin // more text to print printer.newPage; fmtRange.chrg.cpMin := nextChar; End; { If } Until nextchar >= MaxLen; // Free cached information richedit1a.Perform( EM_FORMATRANGE, 0, 0); end; printed:=true; if cap1<> '' then begin richedit1a.lines.delete(richedit1a.lines.count-1); richedit1a.lines.delete(richedit1a.lines.count-1); end; finally Printer.EndDoc; end; The relevnt part seems to be this way of ca,culatin the texzt length: TextLen.Flags := GTL_NUMCHARS; TextLen.CodePage := 1200; // Unicode MaxLen := SendMessage(richedit1a.Handle, EM_GETTEXTLENGTHEX, LPARAM(@TextLen), 0); Share this post Link to post