Mega Code Archive

 
Categories / Delphi / Printing
 

RichEdit Print Preview

Title: RichEdit Print Preview Question: How do you display a correct Print-Preview for a common Richedit Control ? Answer: The task seems simple, but is not so easy as it seems. Even Microsoft admits, that there are some problems, as the Microsoft Articles Q142320 PRB: Rich Edit Control Improperly Displays Print Preview Q253262 HOWTO: Store the Contents of an RTF Control in an EMF File show. To display a correct Print-Preview with correct Text-Heights and -Widths, you can draw the RichEdit-Control on a Metafile, and print this Metafile to the Preview-Canvas : // r : Destination Rectangle (on Preview or Printer Canvas) // XInch : Width in Inch of rectangle to print // YInch : Height in Inch of rectangle to print // xalign,yalign : Alignment in horizontal and vertical direction tRichEditAlign = (rTop,rBottom,rCentered); procedure PrintRichEdit(RichEdit : tMyRichEdit; Canvas : tCanvas; r : tRect; XInch,YInch : extended; xalign : tAlignment; yalign : tRicheditAlign); var Meta : tMetafile; MetafileRect : tRect; begin RichEdit.SelStart:=0; RichEdit.SelLength:=Length(RichEdit.Text); RichEdit.Paragraph.Alignment:=xAlign; if (XInch if Canvas Printer.Canvas then begin // Preview with Metafile Meta:=tMetafile.Create; Meta.MMWidth :=round(XInch*2.54*1000); // Unit is 0.01 mm Meta.MMHeight:=round(YInch*2.54*1000); MetafileRect:=Rect(0,0,Meta.MMWidth,Meta.MMHeight); with tMetafileCanvas.Create(Meta,0) do try RichEdit.PrintPreview(Handle,MetafileRect,yAlign); finally free; end; Canvas.StretchDraw(r,Meta); Meta.Free; end else // Printing RichEdit.PrintRect(Printer.Canvas.Handle,r,yAlign); end; To extent the functionality I use here a modified print procedure, that replaces the original the TCustomRichEdit.Print method : tMyRichEdit = class(tRichEdit) public procedure PrintRect(dc : HDC;r : tRect;align : tRichEditAlign); procedure PrintPreview(dc : HDC;r : tRect;align : tRichEditAlign); end; procedure tMyRichEdit.PrintRect(dc : HDC; r : tRect; align : tRichEditAlign); // see Windows SDK in MSDN : // in /Platform SDK/User Interface Services/Windows User Interface/... // .../Controls/Rich Edit Controls var Range: TFormatRange; // defined in Unit RichEdit.pas LogX, LogY, OldMap, h : Integer; SaveRect: TRect; begin FillChar(Range, SizeOf(TFormatRange), 0); with Printer, Range do begin hdc := dc; hdcTarget := dc; LogX := GetDeviceCaps(dc, LOGPIXELSX); LogY := GetDeviceCaps(dc, LOGPIXELSY); // 1 Twip = 1/20 of a Point, a typographic point is 1/72 Inch // (Device-Coordinates / DPI -- Inches) *72 * 20 -- Twips rc.left := round((r.Left / LogX) * 1440); rc.top := round((r.Top / LogY) * 1440); rc.right := round((r.Right / LogX) * 1440); rc.bottom := round((r.Bottom / LogY) * 1440); rcPage := rc; SaveRect := rc; // print entire content chrg.cpMax := -1; // ensure printer DC is in text map mode OldMap := SetMapMode(hdc, MM_TEXT); // flush buffer SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0); try rc := SaveRect; chrg.cpMin := 0; // wParam = 0 : get Text-Height SendMessage(Self.Handle, EM_FORMATRANGE, 0, Longint(@Range)); chrg.cpMin := 0; h:=rc.bottom-rc.top; rc := SaveRect; case align of rBottom : rc.top := SaveRect.Bottom-h-5; // 5 Twips = 0.1 mm rCentered : rc.top := SaveRect.top+ ((SaveRect.Bottom-SaveRect.top)-h) div 2; end; // wParam = 1 : print (only text that fits in region range.rc) SendMessage(Self.Handle, EM_FORMATRANGE, 1, Longint(@Range)); // flush buffer SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0); finally SetMapMode(hdc, OldMap); // restore previous map mode end; end; end; function GetPreciseTextLen(ARichEditCtrl : TRichedit): integer; var gtlex : TGetTextLengthEx; begin with gtlex do begin flags := GTL_PRECISE; codepage := CP_ACP; end; Result := ARichEditCtrl.Perform(EM_GETTEXTLENGTHEX,WPARAM(@gtlex), 0 ); end; procedure tMyRichEdit.PrintPreview(dc : HDC; r : tRect; align : tRichEditAlign); // Extension of tCustomRichEdit.Print in ComCtrls.pas var Range: TFormatRange; // definiert in Unit RichEdit.pas SaveRect: TRect; fx,fy : double; DisplayDC : HDC; dpiX,dpiY,HSize,VSize,HRes,VRes,h,maxlen : integer; begin FillChar(Range, SizeOf(TFormatRange), 0); with Printer, Range do begin hdc := dc; // Calculate correction-factors : // fx = GetDeviceCaps(DC,HorzSize) (Screen-Width in mm) // * GetDeviceCaps(DC,LogPixelsX) (dpi Screen) // / 25.4 (Factor Inch - mm) // / GetDeviceCaps(DC,HorzRes) (Screen-Width in Pixel) // // you find e.g. fx = 1.18 for Screen.Width = 1024, // fx = 1.5 for Screen.Width = 800 DisplayDC := CreateDC('DISPLAY',nil,nil,nil); HSize:=GetDeviceCaps(DC,HorzSize); VSize:=GetDeviceCaps(DC,VertSize); dpiX :=GetDeviceCaps(DC,LogPixelsX); dpiY :=GetDeviceCaps(DC,LogPixelsY); VRes :=GetDeviceCaps(DC,VertRes); HRes :=GetDeviceCaps(DC,HorzRes); if HRes 0 then fx:=HSize*dpiX/25.4/HRes else fx:=1.0; if VRes 0 then fy:=VSize*dpiY/25.4/VRes else fy:=1.0; hdcTarget:=DisplayDC; // convert 0.01 mm to 1 Twip rc.left := round((r.Left * 1440 ) / 2540 ); rc.top := round((r.Top * 1440 ) / 2540 ); rc.right := round((r.Right * 1440 ) / 2540 / fx ); rc.bottom := round((r.Bottom * 1440 ) / 2540 / fy); rcPage := rc; SaveRect := rc; // print entire content chrg.cpMin := 0; chrg.cpMax := -1; MaxLen := GetPreciseTextLen(Self); SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0); // wParam = 0 : just measure Text-Size, don't print SendMessage(Self.Handle, EM_FORMATRANGE, 0, Longint(@Range)); chrg.cpMin := 0; h:=rc.bottom-rc.top; rc := SaveRect; case align of rBottom : rc.top := SaveRect.Bottom-h; rCentered : rc.top := SaveRect.top+ ((SaveRect.Bottom-SaveRect.top)-h) div 2; end; SendMessage(Self.Handle, EM_FORMATRANGE, 1, Longint(@Range)); SendMessage(Self.Handle, EM_FORMATRANGE, 0, 0); DeleteDC(DisplayDC); end; end;