...show a preview for a TRichEdit/ TRxRichEdit?

Author: Robert Dunn
Homepage: http://home.att.net/~robertdunn/Yacs.html

Category: VCL

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 über 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.

 

printed from
www.swissdelphicenter.ch
developers knowledge base