Jump to content
alogrep

Richedit printing code gets stuck in 11.3.

Recommended Posts

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

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 by PeterBelow

Share this post


Link to post

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

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
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
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

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×