Mega Code Archive

 
Categories / Delphi / VCL
 

How to show a preview for a TRichEdit TRxRichEdit

Title: How to show a preview for a TRichEdit/ TRxRichEdit 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 )) 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.