Sunteți pe pagina 1din 6

Tiprire i Previzualizare

Bazele tipririi i previzualizrii n MFC


Cerine pentru acest curs: Contextul de dispozitiv; Arhitectura Document/View, aplicaii tip SDI; Tratarea mesajelor simple de mouse; defilarea orizontal i/sau vertical (opional).

Obiective: determinarea obiectelor folosite de ctre MFC pentru tiprire i previzualizare; determinarea ordinii de apel al funciilor implicate n tiprire i previzualizare; determinarea elementelor ce identific pagina de tiprit, etc.
Lecia se va desfura n laborator. Drept exerciiu independent: s se adauge codul necesar pentru a realiza defilarea orizontal i/sau vertical. Pentru nvarea manevrrii contextului de dispozitiv se poate propune construirea unei aplicaii care s deseneze graficul unei funcii. Se vor avea n vedere toate situaiile posibile unde poate fi desenat graficul funciei astfel nct s fie ocupat ct mai bine zona client (pentru un grafic ce apare numai n cadranul IV se va desena numai acest cadran i graficul, etc.). Vom exemplifica tiprirea i vizualizarea pe baza unui exemplu. Se creaz o aplicaie cu arhitectura Document/View i tip SDI (restul setrilor rmnnd cele implicite) i cu suport pentru print/preview. Numele proiectului este Print1. Prima modificare. n funcia CPrint1View::OnDraw() adugm: pDC->Rectangle(20, 20, 220, 220); Se va desena un dreptunghi (cu mrimile msurate n pixeli). (20,20) reprezint colul din stnga sus al dreptunghiului, iar (220,220) reprezint colul din dreapta jos al dreptunghiului. Deci mrimea laturilor dreptunghiului este 200 pe 200 pixeli. Colul din stnga sus al zonei client are coordonatele (0,0), axa Ox este pe orizontal, axa Oy este pe vertical. Aceast aplicaie poate produce vizualizarea i tiprirea documentului.

Scalarea
Documentul listat i cel afiat pe ecran nu are aceeai dimensiune (nu arat la fel) pentru c imprimanta folosete unitatea de msur, dots, iar pe ecran se folosete pixelul, i acestea au mrimi diferite (200 dots # 200 pixeli). Acest lucru este descris de modul de mapare (implicit MM_TEXT). Dac dorim s scalm imaginea tiprit la o anumit dimensiune, trebuie s alegem diferite moduri de mapare.

Moduri de mapare
Mode MM_HIENGLISH MM_HIMETRIC MM_ISOTROPIC MM_LOENGLISH MM_LOMETRIC MM_TEXT MM_TWIPS Unit 0.001 inch 0.01 millimeter User-defined 0.01 inch 0.1 millimeter Device pixel 1/1440 inch X Increases right Increases right User-defined Increases right Increases right Increases right Increases right Y Increases up Increases up User-defined Increases up Increases up Increases down Increases up

Lucrul cu grafice n modul MM_TEXT devine o problem cnd imprimantele i ecranele au un numr diferit de dots/pixeli pe pagin. Un mod de mapare mai bun pentru lucrul cu grafice este MM_LOENGLISH, care folosete ca unitate de msur a suta parte dintr-un inch. Pentru a folosi acest mod de mapare, folosim funcia SetMapMode(): pDC->SetMapMode(MM_LOENGLISH); pDC->Rectangle(20, -20, 220, -220); Atentie la originea axelor i la sensul acestora (creterea luix i a lui y) Cnd vom tipri documentul de mai sus vom obine un dreptunghi cu laturile exact de 2 inch.

Tiprirea mai multor pagini


MFC trateaz tiprirea documentului (mai puin bitmap-uri). Funcia OnDraw() din clasa pentru vizualizare realizeaz desenarea pe ecran ct i tiprirea la imprimant. Lucrurile se complic cnd documentul are mai multe pagini sau alte tratri speciale (informaii de nceput i de sfrit de pagin). Exemplificare: Vom modifica aplicaia astfel nct s desenm mai multe dreptunghiuri care s nu ncap pe o pagin. Adugm o variabil membru (int m_numrects) la clasa document care va memora numrul de dreptunghiuri care se vor desena i pe care o iniializm n constructor. Pe mesajul WM_LBUTTONDOWN vom incrementa aceast variabil, iar pe mesajul WM_RBUTTONDOWN vom decrementa aceast variabil. Codul pentru cele dou funcii este dat mai jos:

print1View.cpp --CPrint1View::OnLButtonDown()
void CPrint1View::OnLButtonDown(UINT nFlags, CPoint point) { CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); pDoc->m_numRects++; Invalidate(); CView::OnLButtonDown(nFlags, point); }

print1View.cpp --CPrint1View::OnRButtonDown()
void CPrint1View::OnRButtonDown(UINT nFlags, CPoint point) { CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (pDoc->m_numRects > 0) { pDoc->m_numRects--; Invalidate(); } CView::OnRButtonDown(nFlags, point); }

Rescriem funcia OnDraw() astfel: print1View.cpp --CPrint1View::OnDraw()


void CPrint1View::OnDraw(CDC* pDC) { CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here pDC->SetMapMode(MM_LOENGLISH); char s[10]; wsprintf(s, "%d", pDoc->m_numRects); pDC->TextOut(300, -100, s); for (int x=0; x<pDoc->m_numRects; ++x) { pDC->Rectangle(20, -(20+x*200), 200, -(200+x*200)); } } Vedem ce se ntmpl n PrintPreview. Codul din PrintPreview (n acest moment) nu tie cum s afieze mai multe pagini. Determinarea numrului de pagini se face n funcia OnBeginPrinting().

print1View.cpp --CPrint1View::OnBeginPrinting()
void CPrint1View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo) { CPrint1Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); int pageHeight = pDC->GetDeviceCaps(VERTRES); int logPixelsY = pDC->GetDeviceCaps(LOGPIXELSY); int rectHeight = (int)(2.2 * logPixelsY); int numPages = pDoc->m_numRects * rectHeight / pageHeight + 1; pInfo->SetMaxPage(numPages); } OnBeginPrinting() are doi parametri: un pointer la un context de dispoztiv al imprmantei i un pointer la un obiect CPrintInfo. Pentru c versiunea implicit nu se refer la aceti doi pointeri, numele parametrilor sunt comentai pentru a preveni mesajele de avertizare la compilare. void CPrint1View::OnBeginPrinting(CDC* /*pDC*/ , CPrintInfo* /*pInfo*/) Pentru a seta numrul paginii, trebuie s accesm ambele obiecte CDC* i CPrintInfo, deci va trebui s scoatem comentariile de la ceti doi parametri. Trebuie s avem urmtoarele informaii: 1. nlimea paginii; 2. numrul de dots pe inch. nlimea paginii o obinem printr-un apel al funciei GetDviceCaps(), care furnizeaz informaii despre posibilitile contextului de dispozitiv. Avem nevoie de rezoluia vertical (numrul de dots tipribili de la nceputul paginii pn la sfritul ei), deci vom furniza ca parametru constanta VERTRES, n funcia GetDeviceCaps(). Constanta HORZRES n aceeai funcie ne furnizeaz rezoluia orizontal. GetDeviceCaps() accept un numr de 29 de constante diferite (a se vedea help-ul). n exemplul nostru, pentru a ti cte dreptunghiuri ncap pe o pagin, trebuie s tim nlimea dreptunghiului n dots, deci va trebui s mprim dots pe pagin la dots pe dreptunghi. tim c fiecare dreptunghi este nalt de 2 inch cu un spaiu de 20/100 ntre fiecare dreptunghi. Distana total de la nceputul desenrii unui dreptunghi pn la urmtorul este de 2.2 inch. Apelul GetDeviceCaps(LOGPIXELSY) ne d numrul de dots pe inch pentru imprimant (care este ataat la sistem)., care nmulit cu 2.2 ne d dots pe dreptunghi.

Rulnd aplicaia vom observa c dei n previzualizare avem dou pagini, la listare vom obine pagina 1 de dou ori. Trecerea de la o pagin la alta este urmtorul pas.

Setarea originii
Va trebui s furnizm informaia privitoare la nceputul unei noi pagini. Pentru acest lucru, vom rescrie funcia OnPrepareDC(). Adugm aceaceast funcie la clasa de vizualizare cu ClassWizard. Codul este:

print1View.cpp --CPrint1View::OnPrepareDC()
void CPrint1View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { if (pDC->IsPrinting()) { int pageHeight = pDC->GetDeviceCaps(VERTRES); int originY = pageHeight * (pInfo->m_nCurPage - 1); pDC->SetViewportOrg(0, -originY); } CView::OnPrepareDC(pDC, pInfo); } Cadrul de lucru MFC, apeleaz aceast funcie nainte de afia datele pe ecran sau a le scrie la imprimant. Acelai cod realizeaz previzualizarea ct i tiprirea datelor. Dac aplicaia previzualizeaz datele nu este nevoie s schimbm procesarea efectuat de OnPrepareDC(). Cod necesar este numai pentru tiprire, de aceea folosim funcia IsPrinting() pentru a determina dac aciunea este de tiprire sau nu. Dac tiprim documentul, trebuie s determinm care pagin trebuie tiprit, adic s stabilim pagina curent. Pentru aceasta avem nevoie de nlimea n dots a paginii de tiprit, deci un nou apel al funciei GetDeviceCaps(). n continuare trebuie s detrerminm originea noului viewport (poziia coordonatelor (0,0)) pentru afiare. Schimbnd originea, dm informaia necesar cadrului de lucru MFC, pentru a ti de unde s nceap tiprirea datelor. Pentru prima pagin originea este (0,0); pentru pagina a doua, originea este mutat n jos cu numrul de dots pe pagin. n general, componenta vertical este mrimea paginii nmulit cu pagina curent minus 1 . Numrul paginii este meninut ntr-o variabil membru a clasei CPrintInfo. Dup ce calculm noua origine, vom seta aceasta n cadrul contextului de dispozitiv apelnd funcia SetViewportOrg(). Se va rula aplicaia cu aceste ultime modificri efectuate.

MFC i Tiprirea
Alte funcii importante n procesul de tiprire sunt:

Printing Functions of a View Class


Function OnBeginPrinting() OnDraw() OnEndPrinting() OnPrepareDC() OnPreparePrinting() OnPrint() Description Override this function to create resources, such as fonts, that you need for printing the document. You also set the maximum page count here. This function serves triple duty, displaying data in a frame window, a print preview window, or on the printer, depending on the device context sent as the function's parameter. Override this function to release resources created in OnBeginPrinting(). Override this function to modify the device context used to display or print the document. You can, for example, handle pagination here. Override this function to provide a maximum page count for the document. If you don't set the page count here, you should set it in OnBeginPrinting(). Override this function to provide additional printing services, such as printing headers and footers, not provided in OnDraw().

Pentru a tipri documentul se apeleaz mai nti OnPreparePrinting() care apeleaz DoPreparePrinting() care la rndul ei este responsabil pentru afiarea boxei de dialog Print i creaz contextul de dispozitiv pentru imprimanta selectat.

print1View.cpp --CPrint1View::OnPreparePrinting() as Generated by AppWizard


BOOL CPrint1View::OnPreparePrinting(CPrintInfo* pInfo)

{ // default preparation return DoPreparePrinting(pInfo); } Folosind pointerul la obiectul CPrintInfo, putem face aici diverse iniializri. Trebuie cercetat clasa CPrintInfo.

Members of the CPrintInfo Class


Member SetMaxPage() SetMinPage() GetFromPage() GetMaxPage() GetMinPage() GetToPage() m_bContinuePrinting m_bDirect m_bPreview m_nCurPage m_nNumPreviewPages m_pPD m_rectDraw m_strPageDesc Description Sets the document's maximum page number. Sets the document's minimum page number. Gets the number of the first page that users selected for printing. Gets the document's maximum page number, which may be changed in OnBeginPrinting(). Gets the document's minimum page number, which may be changed in OnBeginPrinting(). Gets the number of the last page users selected for printing. Controls the printing process. Setting the flag to FALSE ends the print job. Indicates whether the document is being directly printed. Indicates whether the document is in print preview. Holds the current number of the page being printed. Holds the number of pages (1 or 2) being displayed in print preview. Holds a pointer to the print job's CPrintDialog object. Holds a rectangle that defines the usable area for the current page. Holds a page-number format string.

Cnd funcia DoPreparePrinting() afieaz boxa de dialog Print, utilizatorul poate seta o parte din datele membru ale clasei CPrintInfo. n mod obinuit, ultimul apel trebuie fcut pentru SetMaxPage() nainte ca DoPreparePrinting() s afieze boxa de dialog Print. Dac nu putem determina numrul de pagini pn cnd nu avem un DC pentru imprimanta selectat, trebuie s ateptm pn cnd obinem acest context de dispozitiv. n mod normal contextul de dispozitiv al imprimantei se creaz la selecia acesteia n cadrul acestei boxe de dialog. Dup OnPreparePrinting(CDC*, CPrintInfo*), MFC apeleaz OnBeginPrinting(), care este un alt loc potrivit pentru a seta numrul maxim de pagini, dar i locul pentru a crea resurse, cum ar fi fonturi, necesare pentru a completa jobul de tiprire. n continuare, MFC apeleaz OnPrepareDC() pentru prima pagin a documentului. Aceasta constituie nceputul buclei care se execut o dat pentru fiecare pagin a documentului. In OnPrepareDC() putem controla care parte din ntreg documentul se tiprete, ca fiind pagina curent. Aici vom seta originea viewportului pentru document. Dup OnPrepareDC(), MFC apeleaz OnPrint() pentru a tipri pagina curent. n mod normal, OnPrint() apeleaz OnDraw() cu un parametru pointer la CDC, care automat redirecteaz ieirea spre imprimant. Putem rescrie OnPrint() pentru a controla modul cum documentul este tiprit. Putem tipri headers i footers n OnPrint() i apoi s apelm versiunea de baz a lui OnDraw() pentru a tipri pagina curent. Pentru a preveni vesriunea din clasa de baz ca s nu rescrie headers i footers, restricionm zona tipribil prin setarea variabilei m_rectDraw din obiectul CPrintInfo la un dreptunghi care nu acopr headers i footers.

Versiune posibil OnPrint() cu Headers i Footers


void CPrint1View::OnPrint(CDC* pDC, CPrintInfo* pInfo) { // TODO: Add your specialized code here and/or call the base class // Call local functions to print a header and footer. PrintHeader(); PrintFooter(); CView::OnPrint(pDC, pInfo); } Se poate renuna la apelul lui OnDraw() n OnPrint(), creind cod suplimentar, ca n exemplul de mai jos.

Versiune posibil OnPrint() fr OnDraw()


void CPrint1View::OnPrint(CDC* pDC, CPrintInfo* pInfo) { // TODO: Add your specialized code here and/or call the base class // Call local functions to print a header and footer. PrintHeader(); PrintFooter(); // Call a local function to print the body of the document. PrintDocument(); } Dup ce MFC a terminat de tiprit ultima pagin, apeleaz funcia OnEndPrinting(), unde putem distruge orice resurs de care nu mai avem nevoie.