//--------------------------------------------------------------------------- #include #pragma hdrstop #include "PreviewForm.h" #include "mainfrm.h" #include "TntSystem.hpp" #include //--------------------------------------------------------------------------- // include this... #include //--------------------------------------------------------------------------- // thanks to Scott Proper for finding several errors where the code // used 1400 instead of the correct 1440. //--------------------------------------------------------------------------- // standard template library is used for to build a vector (or table) of // page offsets // /* typedef struct tagTPageOffset { long int Start; long int End; RECT rendRect; } TPageOffsets; */ //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TPreviewForm *PreviewForm; //--------------------------------------------------------------------------- // custom panel onto which we can draw // class TPreviewPanel : public TPanel { public: __fastcall virtual TPreviewPanel(Classes::TComponent* AOwner) : TPanel(AOwner) { }; __fastcall virtual ~TPreviewPanel(void) { }; __fastcall virtual void Paint(void) { TPanel::Paint(); PreviewForm->DrawRichEdit(); }; __property Canvas; }; //--------------------------------------------------------------------------- __fastcall TPreviewForm::TPreviewForm(TComponent* Owner) : TForm(Owner) { PreviewPanel = new TPreviewPanel(this); PreviewPanel->Parent = Panel1; PreviewPanel->Color = clWhite; this->WindowState = wsMaximized; currPage = 0; } //--------------------------------------------------------------------------- __fastcall TPreviewForm::~TPreviewForm(void) { if (PreviewPanel) delete PreviewPanel; FPageOffsets.erase(FPageOffsets.begin(), FPageOffsets.end()); } //--------------------------------------------------------------------------- // size the panel to dimensions that approximate the page scaled // to fit within the window. // void __fastcall TPreviewForm::FormResize(TObject *Sender) { // get the printer dimensions. int wPage = ::GetDeviceCaps(Printer()->Handle, PHYSICALWIDTH); int hPage = ::GetDeviceCaps(Printer()->Handle, PHYSICALHEIGHT); // get the client window dimensions. int wClient = ClientWidth; int hClient = ClientHeight; // initially adjust width to match height wClient = ::MulDiv(ClientHeight, wPage, hPage); // if that doesn't fit, then do it the other way if (wClient > ClientWidth) { wClient = ClientWidth; hClient = ::MulDiv(ClientWidth, hPage, wPage); // center the page in the window PreviewPanel->Top = (ClientHeight - hClient) >> 1; // divide by two } else // center the page in the window PreviewPanel->Left = (ClientWidth - wClient) >> 1; // divide by two // now set size of panel PreviewPanel->Width = wClient; PreviewPanel->Height = hClient; } //--------------------------------------------------------------------------- // sample code to demonstrate implementing a print preview window. the code, // as written, uses hard-coded two-inch margins (as above) and displays only // the first page. the goal of the example is to demonstrate rendering a // a page of text, in this case the first page only, to a window. the // window can be resized by the user and will be rescaled to approximate // the printed page. to extend this into a useful class, you will need to // add code and buttons (or other means) to move between pages. refer to the // printing code in the main form for code that will, with a little insight // and effort, help you in this effort. in particular, the printing code // demonstrates how to build a table of offsets corresponding to the printed // pages. // // here's an obvious tip: there is a considerable amount of duplicated code, // between the printing function in the main form and the code in the // following routine, that the savy programmer will consolidate into useful // subroutines. // void TPreviewForm::DrawRichEdit(void) { //----------------------------------------------------------------------- // *** same as printing version *** //----------------------------------------------------------------------- float leftMargin = StrToFloat(PrintForm->editLeft->Text), rightMargin = StrToFloat(PrintForm->editRight->Text), topMargin = StrToFloat(PrintForm->editTop->Text), bottomMargin = StrToFloat(PrintForm->editBottom->Text); int wPage = ::GetDeviceCaps(Printer()->Handle, PHYSICALWIDTH); int hPage = ::GetDeviceCaps(Printer()->Handle, PHYSICALHEIGHT); int xPPI = ::GetDeviceCaps(Printer()->Handle, LOGPIXELSX); int yPPI = ::GetDeviceCaps(Printer()->Handle, LOGPIXELSY); int wTwips = ::MulDiv(wPage, 1440, xPPI); int hTwips = ::MulDiv(hPage, 1440, yPPI); RECT pageRect; pageRect.left = pageRect.top = 0; pageRect.right = wTwips; pageRect.bottom = hTwips; RECT rendRect; rendRect.left = rendRect.top = 0; rendRect.right = pageRect.right - 1440 * (leftMargin + rightMargin); rendRect.bottom = pageRect.bottom - 1440 * (topMargin + bottomMargin); TPageOffsets po; po.Start = 0; //----------------------------------------------------------------------- // differences from the printing version start here (although, even // the following has considerable similarity). //----------------------------------------------------------------------- // we will need a few DCs before it is over. note also that this // function is called inside of the TPreviewPanel Paint() handler. HDC hdcDesktop = ::GetWindowDC(::GetDesktopWindow()); HDC hdcCanvas = dynamic_cast(PreviewPanel)->Canvas->Handle; HDC hdcPrinter = Printer()->Handle; FPageOffsets.erase(FPageOffsets.begin(), FPageOffsets.end()); // initialize the formatting data. TFormatRange fr; fr.hdc = hdcDesktop; fr.hdcTarget = hdcPrinter; fr.chrg.cpMin = po.Start; fr.chrg.cpMax = -1; // get the size of the text in the control (use EM_GETTEXTLENEX for RE 2.0). int lastOffset = ::SendMessage(rtfPrint->Handle, WM_GETTEXTLENGTH, 0, 0); // clear the formatting buffer. ::SendMessage(rtfPrint->Handle, EM_FORMATRANGE, (WPARAM) 0, (LPARAM) 0); // save the canvas DC and prepare to scale. SaveDC(hdcCanvas); ::SetMapMode(hdcCanvas, MM_TEXT); // set to device units ::SetMapMode(hdcCanvas, MM_ANISOTROPIC); // inherits prior mapping mode ::SetMapMode(hdcPrinter, MM_TEXT); // set to device units // set window extent to width/height of printer in printer device units. ::SetWindowExtEx(hdcCanvas, pageRect.right, pageRect.bottom, NULL); // scale window extent to width/height of printer in desktop device units. int xDesktopPPI = ::GetDeviceCaps(hdcDesktop, LOGPIXELSX); int yDesktopPPI = ::GetDeviceCaps(hdcDesktop, LOGPIXELSY); ::ScaleWindowExtEx(hdcCanvas, xDesktopPPI, 1440, yDesktopPPI, 1440, NULL); // set viewport to width/height of PreviewPanel in device units. ::SetViewportExtEx(hdcCanvas, PreviewPanel->ClientWidth, PreviewPanel->ClientHeight, NULL); // now, here is one that I really do not get: we have to reduce the // horizontal range a little for the text (wrap) to exactly match. // this may be due to two things: (1) the font scaling that Windows // to make screen fonts appear about the same size at a distance as // the printed fonts do close up or (2) the adjustment to the rendering // rectangle that Rich Edit may or may not do to adjust for the non- // printable portion of the printed page. although (1) strikes me as // perhaps more likely, the latter is easier to do and seems to work // in other projects. if you figure this one out, please, please // let me know. anyway, we will assume that the vertical adjustment // is also required. oh, and we are working in twips. int xPrinterOffset = ::MulDiv(::GetDeviceCaps(hdcPrinter, PHYSICALOFFSETX), 1440, xPPI); int yPrinterOffset = ::MulDiv(::GetDeviceCaps(hdcPrinter, PHYSICALOFFSETY), 1440, yPPI); rendRect.left += xPrinterOffset >> 1; rendRect.right -= xPrinterOffset - (xPrinterOffset >> 1); rendRect.top += yPrinterOffset >> 1; rendRect.bottom -= yPrinterOffset - (yPrinterOffset >> 1); // adjust output origin for two inch margin. int xOffset = ::MulDiv(PreviewPanel->ClientWidth * leftMargin, 1440, pageRect.right); int yOffset = ::MulDiv(PreviewPanel->ClientHeight * topMargin, 1440, pageRect.bottom); ::SetViewportOrgEx(hdcCanvas, xOffset, yOffset, NULL); // loop through data to format and build page offset table. do { // set up remaining format data. fr.rc = rendRect; // RE returns something useful here... fr.rcPage = pageRect; po.Start = fr.chrg.cpMin; // format range with render set to false. fr.chrg.cpMin = ::SendMessage(rtfPrint->Handle, EM_FORMATRANGE, (WPARAM) 0, (LPARAM) &fr); // I don't know why I need to do this, but there is an apparent bug in the TRXRichEdit control // and it doesn't handle the last page properly if there are more than 3 pages of text. if(fr.chrg.cpMin == po.End) fr.chrg.cpMin = lastOffset; po.End = fr.chrg.cpMin - 1; po.rendRect = fr.rc; // and where is this documented? if(fr.chrg.cpMin < lastOffset) FPageOffsets.push_back(po); } while (fr.chrg.cpMin != -1 && fr.chrg.cpMin < lastOffset); // at this point, FPageOffsets.size() is the page count which will // always be at least one. pageCount is not used here since we // display only the first page. // int pageCount = FPageOffsets.size(); // set the rendering device to the scaled DC and the target to null. fr.hdc = hdcCanvas; fr.hdcTarget = 0; // set up remaining format data. fr.rc = FPageOffsets[currPage].rendRect; // documented where? fr.rcPage = pageRect; fr.chrg.cpMin = FPageOffsets[currPage].Start; fr.chrg.cpMax = FPageOffsets[currPage].End; // format range with render set to true. fr.chrg.cpMin = ::SendMessage(rtfPrint->Handle, EM_FORMATRANGE, (WPARAM) 1, (LPARAM) &fr); // clean up. ::RestoreDC(hdcCanvas, -1); ::ReleaseDC(::GetDesktopWindow(), hdcDesktop); ::SendMessage(rtfPrint->Handle, EM_FORMATRANGE, (WPARAM) 0, (LPARAM) 0); UpdatePageNums(); } //--------------------------------------------------------------------------- void __fastcall TPreviewForm::FormShow(TObject *Sender) { rtfPrint = PrintForm->rtfPrint; //UpdatePageNums(); } //--------------------------------------------------------------------------- void __fastcall TPreviewForm::btnFwdClick(TObject *Sender) { if(currPage < (FPageOffsets.size() - 1)){ currPage++; PreviewPanel->Repaint(); //UpdatePageNums(); } } //--------------------------------------------------------------------------- void __fastcall TPreviewForm::btnBackClick(TObject *Sender) { if(currPage > 0){ currPage--; PreviewPanel->Repaint(); //UpdatePageNums(); } } //--------------------------------------------------------------------------- void __fastcall TPreviewForm::btnEndClick(TObject *Sender) { if(currPage != (FPageOffsets.size() - 1)){ currPage = (FPageOffsets.size() - 1); PreviewPanel->Repaint(); //UpdatePageNums(); } } //--------------------------------------------------------------------------- void __fastcall TPreviewForm::btnBeginClick(TObject *Sender) { if(currPage != 0){ currPage = 0; PreviewPanel->Repaint(); //UpdatePageNums(); } } //--------------------------------------------------------------------------- void __fastcall TPreviewForm::btnCloseClick(TObject *Sender) { ModalResult = mrCancel; } //--------------------------------------------------------------------------- void TPreviewForm::UpdatePageNums() { AnsiString strPages = _tr("Page") + " "; strPages += IntToStr(currPage + 1); strPages += " "+ _tr("of") + " "; strPages += IntToStr(FPageOffsets.size()); editPages->Text = strPages; Caption = _tr("Print Preview") + " " + strPages; } void __fastcall TPreviewForm::btnPrintClick(TObject *Sender) { ModalResult = mrOk; } //---------------------------------------------------------------------------