首页  编辑  

RichEdit的Preview

Tags: /超级猛料/VCL/Memo&Edit&Richedit/RichEdit、RxRichEdit/   Date Created:

show a preview for a TRichEdit/ TRxRichEdit?

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

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.

------------------------------------------------------------------------------

...print a rtf file and specify a page range to be printed?

unit Unit1;

interface

uses

 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

 Dialogs, StdCtrls, RichEdit, RxRichEd, ExtCtrls, Printers;

type

 TPageOffset = record

   mStart,

   mEnd: Integer;

   rendRect: TRect;

 end;

 TForm1 = class(TForm)

   Panel1: TPanel;

   Editor: TRxRichEdit;

   PrintBtn: TButton;

   PreviewBtn: TButton;

   CloseBtn: TButton;

   procedure PrintBtnClick(Sender: TObject);

   procedure PreviewBtnClick(Sender: TObject);

   procedure CloseBtnClick(Sender: TObject);

   procedure FormShow(Sender: TObject);

 private

   { Private-Deklarationen }

 public

   { Public-Deklarationen }

 end;

var

 Form1: TForm1;

implementation

uses Unit2;

{$R *.dfm}

procedure TForm1.PrintBtnClick(Sender: TObject);

var  

 wPage, hPage, xPPI, yPPI, wTwips, hTwips: integer;

 pageRect, rendRect: TRect;

 po: TPageOffset;

 fr: TFormatRange;

 lastOffset, currPage, pageCount: integer;

 xOffset, yOffset: integer;

 FPageOffsets: array of TPageOffset;

 TextLenEx: TGetTextLengthEx;

 firstPage: boolean;

begin

 //First, get the size of a printed page in printer device units

 wPage := GetDeviceCaps(Printer.Handle, PHYSICALWIDTH);

 hPage := GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT);

 //Next, get the device units per inch for the printer

 xPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSX);

 yPPI := GetDeviceCaps(Printer.Handle, LOGPIXELSY);

 //Convert the page size from device units to twips

 wTwips := MulDiv(wPage, 1440, xPPI);

 hTwips := MulDiv(hPage, 1440, yPPI);

 //Save the page size in twips

 with pageRect do  

 begin

   Left := 0;

   Top := 0;

   Right := wTwips;

   Bottom := hTwips

 end;

 //Next, calculate the size of the rendering rectangle in twips

 //Rememeber - two inch margins are hardcoded, so the below code

 //reduces the width of the output by four inches

 with rendRect do  

 begin

   Left := 0;

   Top := 0;

   Right := pageRect.Right - (1440 * 4);

   Bottom := pageRect.Bottom - (1440 * 4)

 end;

 //Define a single page and set starting offset to zero

 po.mStart := 0;

 //Define and initialize a TFormatRange structure. This structure is passed

 //to the TRichEdit with a request to format as much text as will fit on a

 //page starting with the chrg.cpMin offset and ending with the chrg.cpMax.

 //Initially, we tell the RichEdit control to start at the beginning

 //(cpMin = 0) and print as much as possible (cpMax = -1). We also tell it

 //to render to the printer

 with fr do  

 begin

   hdc := Printer.Handle;

   hdcTarget  := Printer.Handle;

   chrg.cpMin := po.mStart;

   chrg.cpMax := -1;

 end;

 //In order to recognize when the last page is rendered, we need to know how

 //much text is in the control.

 if RichEditVersion >= 2 then  

 begin

   with TextLenEx do  

   begin

     flags := GTL_DEFAULT;

     codepage := CP_ACP;

   end;

   lastOffset := SendMessage(Editor.Handle, EM_GETTEXTLENGTHEX, wParam(@TextLenEx), 0)

 end

 else  

   lastOffset := SendMessage(Editor.Handle, WM_GETTEXTLENGTH, 0, 0);

 //As a precaution, clear the formatting buffer

 SendMessage(Editor.Handle, EM_FORMATRANGE, 0, 0);

 //Printers frequently cannot print at the absolute top-left position on the

 //page. In other words, there is usually a minimum margin on each edge of the

 //page. When rendering to the printer, RichEdit controls adjust the top-left

 //corner of the rendering rectangle for the amount of the page that is

 //unprintable. Since we are printing with two-inch margins, we are presumably

 //already within the printable portion of the physical page.

 SaveDC(fr.hdc);

 SetMapMode(fr.hdc, MM_TEXT);

 xOffset := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX);

 yOffset := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY);

 xOffset := xOffset + MulDiv(1440 + 1440, xPPI, 1440);

 yOffset := yOffset + MulDiv(1440 + 1440, yPPI, 1440);

 SetViewportOrgEx(fr.hdc, xOffset, yOffset, nil);

 //Now we build a table of page entries, one entry for each page that would be

 //printed.

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

 pageCount := Length(FPageOffsets);

 ShowMessage(Format('Es wurde %d Seiten ermittelt', [pageCount]));

 SendMessage(Editor.Handle, EM_FORMATRANGE, 0, 0);

 RestoreDC(fr.hdc, - 1);

 //Now, we are almost ready to actually print.

 Printer.BeginDoc;

 fr.hdc := Printer.Handle;

 fr.hdcTarget := Printer.Handle;

 SaveDC(fr.hdc);

 SetViewportOrgEx(fr.hdc, xOffset, yOffset, nil);

 //Ok, here we go to print

 firstPage := True;

 //At this point you can select from page and to page

 currPage := 0;  //Print from the first page

 pageCount := 1;  //Only One page for testing

 while (currPage < pageCount) do  

 begin

   if firstPage then firstPage := False

   else  

     Printer.NewPage;

   SetViewportOrgEx(fr.hdc, xOffset, yOffset, nil);

   fr.rc := FPageOffsets[currPage].rendRect;

   fr.rcPage := pageRect;

   fr.chrg.cpMin := FPageOffsets[currPage].mStart;

   fr.chrg.cpMax := FPageOffsets[currPage].mEnd;

   fr.chrg.cpMin := SendMessage(Editor.Handle, EM_FORMATRANGE, 1, Longint(@fr));

   Inc(currPage);

 end;

 //At this point, we have finished rendering the contents of the RichEdit

 //control. Now we restore the printer's HDC settings and tell Windows that

 //we are through printing this document

 RestoreDC(fr.hdc, - 1);

 Printer.EndDoc;

 //Finally, we clear the RichEdit control's formatting buffer and delete

 //the saved page table information

 fr.chrg.cpMin := SendMessage(Editor.Handle, EM_FORMATRANGE, 0, 0);

 Finalize(FPageOffsets);

 //That's it

end;

procedure TForm1.PreviewBtnClick(Sender: TObject);

begin

 PreviewForm.ShowModal

end;

procedure TForm1.CloseBtnClick(Sender: TObject);

begin

 Close

end;

procedure TForm1.FormShow(Sender: TObject);

begin

 Editor.Lines.LoadFromFile('Exceltabelle.rtf');

end;

end.