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.