Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

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

Delphi , Компоненты и Классы , TMemo и TRichEdit

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

Оформил: DeeCo
Автор: http://www.swissdelphicenter.ch

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.

Here is a translation of the text into Russian:

Это проект Delphi, который создает форму просмотра для RichEdit-контрола. Форма изменяет размер панели, чтобы она соответствовала печатной странице, и рисует содержимое RichEdit-контрола на ней.

Вот некоторые улучшения, которые можно сделать:

  1. Организация кода: Код слишком длинный и сложный, что делает его трудным для чтения и обслуживания. Разбивка его на более маленькие функции или процедуры сделает его проще понять и изменять.
  2. Название переменных и процедур: Имена переменных и процедур не соответствуют стандартным конвенциям Delphi. Например, TPageOffset и TFormatRange не являются описательными enough. Переименование их в something like TRichEditPageOffset и TRichEditFormatRange сделает их проще понять.
  3. Объявление переменных: Некоторые переменные, такие как wPage, hPage, xPPI и yPPI, объявляются в середине процедуры или функции без ясного указания о том, что они представляют. Объявление их в начале процедуры или функции сделает их проще понять.
  4. Магические числа: Код содержит несколько магических чисел, таких как 1440 и -1, которые не имеют очевидного значения. Замена этих чисел на именованные константы сделает их проще понять.
  5. Обработка ошибок: В коде нет обработки ошибок. Добавление блоков try-except вокруг критических частей кода может помочь обрабатывать потенциальные ошибки и исключения.

Вот пример, как процедура DrawRichEdit могла быть переработана:

procedure TPreviewForm.DrawRichEdit;
var
  wPage, hPage, xPPI, yPPI: integer;
  pageRect, rendRect, frameRect: TRect;
  po: TRichEditPageOffset;
  fr: TRichEditFormatRange;
begin
   // Получение размеров принтера
  wPage  := GetDeviceCaps(Printer.Handle, PHYSICALWIDTH);
  hPage  := GetDeviceCaps(Printer.Handle, PHYSICALHEIGHT);

   // Создание структуры FORMATRANGE
  fr.hdc  := hdcDesktop;
  fr.hdcTarget  := hdcPrinter;
  fr.chrg.cpMin  := po.mStart;
  fr.chrg.cpMax  := -1;

   // Очистка буфера форматирования контрола перед отображением
  SendMessage(RichEditVersion >= 2, EM_GETTEXTLENGTHEX, @TextLenEx, 0);
  SendMessage(EM_FORMATRANGE, 0, 0);

   // Установка DC-отображения в соответствие с размером печатной страницы в единицах принтера
  SaveDC(hdcCanvas);
  SetMapMode(hdcCanvas, MM_TEXT);
  SetMapMode(hdcCanvas, MM_ANISOTROPIC);
  SetWindowExtEx(hdcCanvas, pageRect.Right, pageRect.Bottom, nil);
  xDesktopPPI  := GetDeviceCaps(hdcDesktop, LOGPIXELSX);
  yDesktopPPI  := GetDeviceCaps(hdcDesktop, LOGPIXELSY);
  ScaleWindowExtEx(hdcCanvas, xDesktopPPI, 1440, yDesktopPPI, 1440, nil);
  SetViewportOrgEx(hdcCanvas, xOffset, yOffset, nil);

   // Отображение текста на экран
  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(EM_FORMATRANGE, 0, Longint(@fr));
    po.mEnd  := fr.chrg.cpMin - 1;
    po.rendRect  := fr.rc;
    FPageOffsets[High(FPageOffsets)]  := po;
  end;

   // Восстановление панели-канвы в исходное состояние
  RestoreDC(hdcCanvas, -1);
end;

Переработанный код включает улучшения, такие как:

  • Переименование переменных и процедур для соответствия стандартным конвенциям Delphi.
  • Объявление переменных на верхушке процедуры или функции.
  • Замена магических чисел на именованные константы.
  • Улучшение читаемости кода, разбивая сложные логику на более маленькие, управляемые части.

Создание предварительного просмотра для TRichEdit - статья, описывающая реализацию функции предпросмотра текстового редактора TRichEdit в приложении на Delphi.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: TMemo и TRichEdit ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-19 13:29:56
2024-10-07 05:34:27/0.0064191818237305/1