Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Сделать предварительный просмотр для TRichEdit

01.01.2007
unit RichEditPreview;
 
 interface
 
 uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
   Dialogs, ExtCtrls, Printers, RichEdit, Menus, ComCtrls, ToolWin;
 
 type
   TPageOffset = record
     mStart, mEnd: Integer;
     rendRect: TRect;
   end;
 
   TPreviewForm = class(TForm)
     Panel1: TPanel;
     Panel2: TPanel;
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
     procedure FormResize(Sender: TObject);
   private
     { Private-Deklarationen }
   public
     { Public-Deklarationen }
     PreviewPanel: TPanel;
     procedure DrawRichEdit;
   end;
 
   TPreviewPanel = class(TPanel)
   private
 
   public
     constructor Create(Owner: TComponent); override;
     destructor Destroy; override;
     procedure Paint; override;
     property Canvas;
   end;
 
 var
   PreviewForm: TPreviewForm;
 
 implementation
 
 uses Unit1, RxRichEd;
 
 {$R *.dfm}
 
 procedure TPreviewForm.FormCreate(Sender: TObject);
 begin
   PreviewPanel := TPreviewPanel.Create(Self);
   PreviewPanel.Parent := Self;
   PreviewPanel.Color := clWhite;
 end;
 
 procedure TPreviewForm.FormDestroy(Sender: TObject);
 begin
   if PreviewPanel <> nil then PreviewPanel.Free
 end;
 
 // We want the TPreviewPanel to approximate the scaled dimensions of the printed page. 
// Whenever the parent 
// form is resized, we need to rescale and center the panel on the form. 
// To do this, add an OnResize event to 
// the form and add the following code: 
 
procedure TPreviewForm.FormResize(Sender: TObject);
 var
    wPage, hPage, wClient, hClient: integer;
 begin
   // get the printer dimensions 
  wPage := GetDeviceCaps(Printer.Handle, PHYSICALWIDTH);
   hPage := GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT);
   // get the client window dimensions. 
  hClient := Panel2.ClientHeight;
   // initially adjust width to match height 
  wClient := MulDiv(Panel2.ClientHeight, wPage, hPage);
   // if that doesn't fit, then do it the other way 
  if wClient > Panel2.ClientWidth then
    begin
     wCLient := Panel2.ClientWidth;
     hClient := MulDiv(Panel2.ClientWidth, hPage, wPage);
     // center the page in the window 
    PreviewPanel.Top := ((Panel2.ClientHeight - hClient) div 2) - Panel1.Height;
   end
   else
    begin
     // center the page in the window 
    PreviewPanel.Left := (Panel2.ClientWidth - wClient) div 2;
     PreviewPanel.Top  := Panel1.Height;
   end;
   // now set size of panel 
  PreviewPanel.Width  := wClient;
   PreviewPanel.Height := hClient
 end;
 
 // The DrawRichEdit() method renders the contents of 
// the control on the preview panel. 
// Much of the code is 
// very close to the code used to print the control in Part 2. 
// The first part of the method is identical to 
// the printing code: 
 
procedure TPreviewForm.DrawRichEdit;
 var
    wPage, hPage, xPPI, yPPI, wTwips, hTwips, currPage: integer;
   pageRect, rendRect, frameRect: TRect;
   po: TPageOffset;
   fr: TFormatRange;
   lastOffset, xOffset, yOffset, xPrinterOffset, yPrinterOffset: integer;
   FPageOffsets: array of TPageOffset;
   TextLenEx: TGetTextLengthEx;
   hdcDesktop, hdcCanvas, hdcPrinter, xDesktopPPI, yDesktopPPI,
   xFactor, yFactor: integer;
 begin
   wPage := GetDeviceCaps(Printer.Handle, PHYSICALWIDTH);
   hPage := GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT);
   xPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
   yPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
   wTwips := MulDiv(wPage, 1440, xPPI);
   hTwips := MulDiv(hPage, 1440, yPPI);
   with pageRect do
    begin
     Left := 0;
     Top := 0;
     Right := wTwips;
     Bottom := hTwips
   end;
   with rendRect do
    begin
     Left := 0;
     Top := 0;
     Right := pageRect.Right - (1440 * 4);
     Bottom := pageRect.Bottom - (1440 * 4)
   end;
   po.mStart := 0;
   // We will be using several device contexts (DCs), 
  // so let's go ahead and create variables for them. 
  hdcDesktop := GetWindowDC(GetDesktopWindow);
   hdcCanvas  := TPreviewPanel(PreviewPanel).Canvas.Handle;
   hdcPrinter := Printer.Handle;
   // Next, define and initialize a FORMATRANGE structure. 
  fr.hdc        := hdcDesktop;
   fr.hdcTarget  := hdcPrinter;
   fr.chrg.cpMin := po.mStart;
   fr.chrg.cpMax := -1;
   // We will need the size of the text in the control. 
  if RichEditVersion >= 2 then
    begin
     with TextLenEx do
      begin
       flags    := GTL_DEFAULT;
       codepage := CP_ACP;
     end;
     lastOffset := SendMessage(Form1.Editor.Handle, EM_GETTEXTLENGTHEX,
       wParam(@TextLenEx), 0)
   end
   else
      lastOffset := SendMessage(Form1.Editor.Handle, WM_GETTEXTLENGTH, 0, 0);
   // Clear the control's formatting buffer before rendering. 
  SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 0, 0);
   // Here is the tricky part. 
  // We need to scale the rendering DC to match the size of the printed page in 
  // printer device units. 
  SaveDC(hdcCanvas);
   SetMapMode(hdcCanvas, MM_TEXT);
   SetMapMode(hdcCanvas, MM_ANISOTROPIC);
   SetMapMode(hdcPrinter, MM_TEXT);
   SetWindowExtEx(hdcCanvas, pageRect.Right, pageRect.Bottom, nil);
   xDesktopPPI := GetDeviceCaps(hdcDesktop, LOGPIXELSX);
   yDesktopPPI := GetDeviceCaps(hdcDesktop, LOGPIXELSY);
   ScaleWindowExtEx(hdcCanvas, xDesktopPPI, 1440, yDesktopPPI, 1440, nil);
   SetViewportExtEx(hdcCanvas, PreviewPanel.ClientWidth, PreviewPanel.ClientHeight, nil);
   // Apparently, the Rich Edit control reduces the width of the 
  // rendering area by the amount of the left 
  // offset to the printable portion of the page when printing. 
  // This is a little odd to me because none of 
  // the Windows API GDI functions care whether you are printing 
  // within the printable portion of the page. 
  // Further, this occurs even though the rendering rectangle is 
  // already within the printable portion of the 
  // page.  Anyway, this does not seem to happen when the rendering 
  // DC is the screen so we need to manually 
  // adjust the rectangle ourselves. 
  xPrinterOffset  := MulDiv(GetDeviceCaps(hdcPrinter, PHYSICALOFFSETX), 1440, xPPI);
   yPrinterOffset  := MulDiv(GetDeviceCaps(hdcPrinter, PHYSICALOFFSETY), 1440, yPPI);
   rendRect.Left   := rendRect.Left + (xPrinterOffset shr 1);
   rendRect.Right  := rendRect.Right - xPrinterOffset - (xPrinterOFfset shr 1);
   rendRect.Top    := rendRect.Top + (yPrinterOffset shr 1);
   rendRect.Bottom := rendRect.Bottom - yPrinterOffset - (yPrinterOFfset shr 1);
   // Remember that we are hardcoding two-inch margins. 
  xOffset := MulDiv(PreviewPanel.ClientWidth shl 1, 1440, pageRect.Right);
   yOffset := MulDiv(PreviewPanel.ClientHeight shl 1, 1440, pageRect.Bottom);
   SetViewportOrgEx(hdcCanvas, xOffset, yOffset, nil);
   // Now we build the table of offsets. 
  // Note that we save the rendering rectangle returned by the format 
  // call.  When the rendering and target devices are the same 
  // (or the target device is set to zero), the 
  // returned rectangle is not really needed. 
  // In that case, you can simply ask the control to print to the 
  // original rendering rectangle.  However, when the devices are different, 
  // the returned rendering rectangle 
  // is sometimes larger than the requested rectangle. 
  // This must be a bug in the Rich Edit control.  We deal 
  // with it by saving the returned value to use when 
  // we actually render the control to the screen. 
  while ((fr.chrg.cpMin <> -1) and (fr.chrg.cpMin < lastOffset)) do
    begin
     fr.rc         := rendRect;
     fr.rcPage     := pageRect;
     po.mStart     := fr.chrg.cpMin;
     fr.chrg.cpMin := SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 0, Longint(@fr));
     po.mEnd       := fr.chrg.cpMin - 1;
     po.rendRect   := fr.rc;
     if High(FPageOffsets) = -1 then SetLength(FPageOffsets, 1)
     else
        SetLength(FPageOffsets, Length(FPageOffsets) + 1);
     FPageOffsets[High(FPageOffsets)] := po
   end;
   // If we were writing a fully working preview function, 
  // we could use FPageOffsets.size() to determine how 
  // many pages had been formatted. 
  // We would then set currPage (below) to the page that we wanted to 
  // display. 
  // In this example, however, we are going to display only the first page. 
  currPage := 0;
   // Now we set the rendering device to the panel's canvas. 
  // Since we have not cleared the formatting buffer, 
  // the target device is not needed, so we set it to zero. 
  // Then we fill in the remaining parts of the 
  // FORMATRANGE structure with the values we saved in FPageOffsets. 
  // Finally, we render the text to the 
  // screen (WPARAM is non-zero). 
  fr.hdc := hdcCanvas;
   fr.hdcTarget  := 0;
   fr.rc := FPageOffsets[currPage].rendRect;
   fr.rcPage := pageRect;
   fr.chrg.cpMin := FPageOffsets[currPage].mStart;
   fr.chrg.cpMax := FPageOffsets[currPage].mEnd;
   fr.chrg.cpMin := SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 1, Longint(@fr));
   // As I mentioned, the text may be drawn outside of the rendering rectangle. 
  // To make that easier to see, 
  // let's draw a rectangle that shows where the rendering rectangle should be 
  SetMapMode(hdcCanvas, MM_TEXT);
   SetViewportOrgEx(hdcCanvas, 0, 0, nil);
   frameRect := rendRect;
   OffsetRect(frameRect, 1440 + 1440, 1440 + 1440);
   xFactor          := MulDiv(PreviewPanel.ClientWidth,
     (pageRect.Right - rendRect.Right) shr 1, pageRect.Right);
   yFactor          := MulDiv(PreviewPanel.ClientHeight,
     (pageRect.Bottom - rendRect.Bottom) shr 1, pageRect.Bottom);
   frameRect.Left   := xFactor;
   frameRect.Right  := PreviewPanel.ClientWidth - xFactor;
   frameRect.Top    := yFactor;
   frameRect.Bottom := PreviewPanel.ClientHeight - yFactor;
   Windows.FrameRect(hdcCanvas, frameRect, GetStockObject(BLACK_BRUSH));
   // To wrap up, we restore the panel's canvas to the original state, 
  // release the desktop DC, clear the Rich 
  // Edit control's formatting buffer, empty the page offset table, 
    and Close the DrawRichEdit() method.RestoreDC(hdcCanvas, - 1);
   ReleaseDC(GetDesktopWindow, hdcDesktop);
   SendMessage(Form1.Editor.Handle, EM_FORMATRANGE, 0, 0);
   Finalize(FPageOffsets);
 end;
 
 (*****************************************************)
 (* Alles uber den Nachfahren von TPanel              *)
 (*****************************************************)
 
 constructor TPreviewPanel.Create(Owner: TComponent);
 begin
   inherited Create(Owner);
 end;
 
 destructor TPreviewPanel.Destroy;
 begin
   inherited Destroy
 end;
 
 procedure TPreviewPanel.Paint;
 begin
   inherited Paint;
   PreviewForm.DrawRichEdit;
 end;
 
 end.

Взято с сайта: https://www.swissdelphicenter.ch