Rozdział 22
Drukowanie i podgląd wydruku
Wzbogacanie standardowej aplikacji o opcje drukowania i podglądu wydruku
Dołączanie dokumentów wielostronicowych
Zabezpieczanie przed zniekształceniami wydruku
Bezpośrednie drukowanie dokumentu z pominięciem szkieletu aplikacji
Drukowanie za pomocą funkcji szkieletu aplikacji
Tworzone przez kreator AppWizard szkielety aplikacji SDI i MDI oferują standardowe narzędzia i
funkcje obsługujące drukowanie i oglądanie podglądu wydruku. Opcje te można wyłączyć na stronie 4
kreatora AppWizard, wyłączając Printing and Print Preview. Pozostawienie ich nie skomplikuje jednak
zbytnio projektu, dlatego nie warto ich usuwać, zwłaszcza że mogą się nam później przydać. Większość
procesów związanych z drukowaniem obsługiwanych jest przez kontekst urządzenia i obiekty GDI
(mówiliśmy o nich w rozdziale 15). Szkielet aplikacji dostarcza nam kontekstu urządzenia obsługującego
drukowanie strony dokumentu; działa on mniej więcej tak samo jak zwykły kontekst urządzenia.
Niezależność obsługi drukowania od zainstalowanej drukarki
Przed pojawieniem się systemu Windows większość systemów operacyjnych nie posiadało
sterowników drukarki przystosowanych do obsługi grafiki. Powodowało to, że twórcy aplikacji
zmuszeni byli tworzyć dla własnych programów komplety sterowników obsługujących
najpopularniejsze drukarki, W ten sposób każda działająca w komputerze aplikacja miała własne
sterowniki dla tych samych drukarek, niepotrzebnie du-plikując zajmujące się w gruncie rzeczy tym
samym fragmenty kodu programu.
564___________________________________ Poznaj Visual C++6
Szczęśliwie wprowadzenie przez system Windows kontekstu urządzenia pozwala obecnie traktować
wewnątrz programu różnego rodzaju urządzenia, takie jak drukarka, ekran komputera czy ploter, tak
jakby były one po prostu dwuwymiarową powierzchnią, na której ma zostać wykonany rysunek. W ten
sposób sterownik urządzenia pisany jest tylko raz, przez producenta danego urządzenia.
PATRZ TAKŻE
Więcej informacji na temat szkieletu aplikacji SDI znaleźć można w rozdziale 12.
* Kompletny opis działania kontekstu urządzenia znaleźć można w rozdziale 13.
Korzystanie ze standardowych funkcji obsługujących drukowanie
Szkielet aplikacji SDI (aplikacji jednodokumentowej) obsługuje drukowanie zawartości
widoku w oparciu o informacje przechowywane w dokumencie. Ponieważ informacje te są
właśnie wyświetlane w widoku, po dodaniu do aplikacji odpowiednich opcji drukowania
wydrukowanie ich nie powinno stanowić większego problemu.
Aby wyświetlić rysunek w widoku, aplikacja przywołuje funkcję onDraw (). Podobna do
niej funkcja OnPrint () przywoływana jest, gdy widok obsługuje informacje związane z
drukowaniem. Bardzo często drukowanie wykonywane jest tutaj za pomocą dokładnie tego
samego kodu co w funkcji OnDraw(). W takiej sytuacji nie ma potrzeby implementowania
funkcji OnPrint(); szkielet aplikacji implementuje ją automatycznie w klasie bazowej CView i
wewnątrz funkcji OnPrint() przyzywa funkcję OnDraw(). Drukarka jest tutaj traktowana tak
samo jak ekran komputera, ponieważ oferuje ona własny kontekst urządzenia zastępujący
kontekst urządzenia ekranu. Funkcja OnDraw() ustala, czy przesłany jej kontekst urządzenia
jest kontekstem urządzenia ekranu, czy też drukarki. Funkcje rysujące, działające w obu
przypadkach, są jednak na tyle podobne, że nawet i bez sprawdzenia program zadziała
prawidłowo.
Standardową obsługę drukowania możemy obejrzeć tworząc za pomocą kreatora
AppWizard aplikację SDI. W tym celu pozostawimy w kroku czwartym kreatora zaznaczoną
opcję Printing and Print Preyiew (oznacza to, że możemy po prostu kliknąć Finish na
pierwszej stronie kreatora), a projekt nazwiemy Printit.
Standardowa obsługa drukowania
Standardowe funkcje umożliwiające obsługę drukowania i podglądu wydruku dostępne są
tylko w aplikacjach SDI i MDI. W aplikacjach opartych na oknach dialogowych obsługę
drukowania trzeba zaprogramować samodzielnie.
Drukowanie i podgląd wydruku 565
Aby zobaczyć jak program działa, będziemy musieli najpierw dostarczyć mu rysunek, który będzie
można wydrukować. Rysunek taki utworzymy za pomocą przedstawionej na listingu 22.1 funkcji
OnDraw klasy CPrintltView (klasy identycznej z jej bazową klasą cview). Na rysunku 22.1 zobaczyć
można jak wyglądać będzie wydruk. Sam składający się z grafiki i tekstu rysunek testowy nie jest taki
ważny, pomoże nam jednak porównać efekt wyświetlony na ekranie z wydrukiem.
Listing 22.1. LST23_1.CPP - tworzenie w funkcji OnDraw przykładowego rysunku do wydrukowania
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void CPrintltView::OnDraw(CDC* pDC) (
CPrintItDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc) ;
// TODO: Tutaj dodaj resztę kodu malującego widok
II ** Zdefiniuj tryb odwzorowywania w jednostkach metrycznych pDC-
>SetMapMode O (MM_LOMETRIC) ;
// ** Zadeklaruj i utwórz czcionkę o wysokości 2,2 cm CFont
fnBig;
fnBig.CreateFont(220,O,O,O,FW_HEAVY,FALSE,FALSE,O,
ANSI_CHARSET,OUT_DEFAULT_PRECI S,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,
FF_SWISS+VARIABLE_PITCH,"Arial") ;
//** Wybierz nową czcionkę i zachowaj poprzednią CFont*
pOldFont = pDC->SelectObject(SfnBig);
//** Zadeklaruj prostokąt obszaru roboczego CRect
rcCIient;
GetCIientRect(SrcCIient) ;
// ** Przekształć na logiczne jednostki odwzorowywania pDC-
>DPtoLP(SrcCIient) ;
// ** Zdefiniuj kilka zmiennych używanych podczas rysowania const
int nPoints = 50;
int xm = rcCIient.Width();
int ym = rcCIient.Height();
double dAspW = xm/(double)nPoints;
double dAspH = ym/(double)nPoints;
566__________ Poznaj Visual C++6
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 )
// ** Wybierz czarne pióro CPen* pOldPen =
(CPen*)pDC->SelectStockObject(BLACK_PEN),
// ** Narysuj linie for(int i=0;i
int xo = (int)(i * dAspW) ;
int yo = (int) (i * dAspH);
pDC->MoveTo(xo,0); @ pDC-
>LineTo(xm,yo); pDC->LineTo(xm-
xo,ym); pDC->LineTo(0,ym-yo);
pDC->LineTo(xo,0);
// ** Przywróć poprzednie pióro pDC-
>SelectObject(pOldPen) ;
// ** Wpisuj tekst od góry pDC-
>SetTextAlign(TA_CENTER+TA_BASELINE) ;
pDC->SetBkMode(TRANSPARENT) ;
// ** Wpisz szary tekst pDC->SetTextColor(RGB(64,64,64)) ;
pDC->TextOut(xm/2,ym/2,"Sample Print");
// ** Przywróć pierwotną czcionkę pDC-
>SelectObject(pOldFont) ;
O Ustawiony został tryb odwzorowywania MM_LOMETRIC, definiujący rozmiar logicznej
jednostki współrzędnych na 1/10 milimetra.
@ Malujemy widoczną na rysunku obok rozetę.
W linii 61 wyświetlamy pośrodku okna tekst "Sample Print" (Przykładowy wydruk).
Drukowanie i podgląd wydruku 567
Rysunek 22.1. Rysunek testujący wydruk w oknie programu Printit
Mimo 'iż funkcja OnDraw () jest dość długa, w jej kodzie nie ma nic zaskakującego. Funkcja rysuje
po prostu wewnątrz obszaru roboczego serię linii, a pośrodku wpisuje ustalony tekst. Warto zauważyć, że
w linii 9 tryb odwzorowywania został zdefiniowany jako MM_LOMETRIC. W ten sposób współrzędne będą
definiowane w jednostkach logicznych równych 1/10 milimetra (patrz rozdział 15).
W linii 13 definiowana jest czcionka o wysokości 2,2 cm, używana później w linii 61 do
namalowania przykładowego tekstu. W liniach 40-50 w oparciu o współrzędne obszaru roboczego
rysujemy seńę linii tworzących rozetę. Szczegóły funkcji rysującej nie są w tym momencie ważne, nasz
przykład jest ilustracją zagadnień związanych z drukowaniem.
Tryby odwzorowywania a ekran komputera
Można zauważyć, że po wyświetleniu rysunku na ekranie czcionka nie będzie miała dokładnie
wysokości 2,2 cm. Dzieje się tak dlatego, że system Windows nie potrafi dopasować się dobrze do
rozdzielczości niektórych typów monitorów (nawet gdy rozdzielczość jest ustawiona prawidłowo w
Panelu sterowania). Sterowniki drukarek są jednak pod tym względem dokładniejsze i na wydruku
czcionka będzie miała wysokość dokładnie 2,2 cm.
Po zbudowaniu i uruchomieniu aplikacji zawierającej przedstawioną tutaj funkcję On-Draw (), na
ekranie wyświetlony zostanie obrazek widoczny na rysunku 22. l.
Pojawia się pytanie: co należy zrobić, aby ten rysunek wydrukować? Praktycznie już nic, ponieważ
aplikacja obsługując drukowanie go sięgnie do funkcji OnDraw () przesyłając jej zamiast kontekstu
urządzenia okna kontekst urządzenia drukarki.
568_____________________________________Poznaj Visual C++ 6
Jeśli teraz w menu File aplikacji Printit wybierzemy polecenie Print Pre^iew, w lewym górnym
rogu zobaczymy pomniejszoną wersję rysunku. Jednak widoczny na ekranie tekst będzie zdecydowanie
za duży i będzie nachodził na rozetę. Nie jest to wina szkieletu aplikacji, funkcja otrzymała po prostu złe
współrzędne, które nie pasują do kontekstu urządzenia drukarki. Problem generuje przyzywana w linii 23
funkcja GetdientRect ().
Funkcja GetdientRect () jest funkcją składową widoku, a nie kontekstu urządzenia. Nie sprawia
jednak problemów przy rysowaniu na ekranie, ponieważ kontekst urządzenia jest tu taki sam jak
rozmiary prostokąta obszaru roboczego okna. Jednak kontekst urządzenia dla drukarki jest mniejszy,
więc rozeta zostanie odpowiednio pomniejszona, podczas gdy czcionka tekstu będzie z uwagi na tryb
odwzorowywania zawsze tych samych rozmiarów (2,2 cm).
PATRZ TAKŻE
* O pokrywaniu funkcji OnDraw () pisaliśmy w rozdziale 15.
Więcej na temat rysowania linii znaleźć można w rozdziale 16. 4
Więcej informacji o rysowaniu tekstu znaleźć w rozdziale 17.
Pokrywanie funkcji OnPrint()
Aby naprawić ten problem należy przesłać funkcji OnDrawO w miejsce prostokąta okna prostokąt
drukarki. Szczęśliwie szkielet aplikacji przyzywa wirtualną funkcję, którą możemy pokryć w widoku
wpisując tam odpowiednie informacje. Jak wspomnieliśmy wcześniej, funkcja ta nosi nazwę OnPrint () i
jest bardzo podobna do funkcji OnDraw (). Kiedy program odmalowuje zawartość okna przywoływana
jest funkcja OnDraw (), a gdy zawartość okna jest drukowana, przyzywana jest funkcja onprint ().
Zastanawiające jest w jaki sposób funkcja OnDraw () wykorzystywana jest do wyświetlania podglądu
wydruku. Standardowa implementacja funkcji OnPrint () w klasie CView przywołuje po prostu funkcję
OnDraw (), przesyłając jej kontekst urządzenia drukarki.
Funkcje wirtualne i polimorfizm
Funkcja wirtualna to funkcja zdefiniowana w klasie bazowej zawierająca często kod wykonujący
pewne standardowe operacje. Wewnątrz klasy wywodzącej się z klasy bazowej możemy zdefiniować
własną wersję tej funkcji, wykonującą oprócz standardowych operacji również zlecone przez nas
zadania. Zawsze gdy przywoływana jest funkcja o danej nazwie (nawet w obiekcie klasy bazowej),
przyzywana też jest funkcja z klasy pochodnej (ang. inherited). Technika ta nosi nazwę pokrywania
funkcji wirtualnych (ang. virtual function ovemding). Funkcja pokrywająca (ang. overri-de) może
również odwołać się do swojej wersji z klasy bazowej bezpośrednio przywołując pokrywaną (ang.
ovemden) funkcję, podając jako jej zakres kompetencji (ang. scope) klasę bazową. W ten sposób
funkcje pokrywające mogą korzystać z możliwości funkcji klasy bazowej. Wirtualne funkcje są w
języku C++ podstawowym sposobem implementacji polimorfizmu (ang. polymorphism).
568_____________________________________Poznaj Visual C++ 6
Jeśli teraz w menu File aplikacji Printit wybierzemy polecenie Print Pre^iew, w lewym górnym
rogu zobaczymy pomniejszoną wersję rysunku. Jednak widoczny na ekranie tekst będzie zdecydowanie
za duży i będzie nachodził na rozetę. Nie jest to wina szkieletu aplikacji, funkcja otrzymała po prostu złe
współrzędne, które nie pasują do kontekstu urządzenia drukarki. Problem generuje przyzywana w linii 23
funkcja GetCiientRect ().
Funkcja GetCiientRect () jest funkcją składową widoku, a nie kontekstu urządzenia. Nie sprawia
jednak problemów przy rysowaniu na ekranie, ponieważ kontekst urządzenia jest tu taki sam jak
rozmiary prostokąta obszaru roboczego okna. Jednak kontekst urządzenia dla drukarki jest mniejszy,
więc rozeta zostanie odpowiednio pomniejszona, podczas gdy czcionka tekstu będzie z uwagi na tryb
odwzorowywania zawsze tych samych rozmiarów (2,2 cm).
PATRZ TAKŻE
O pokrywaniu funkcji OnDraw () pisaliśmy w rozdziale 15. Więcej na
temat rysowania linii znaleźć można w rozdziale 16. 4 Więcej informacji o
rysowaniu tekstu znaleźć w rozdziale 17.
Pokrywanie funkcji OnPrint()
Aby naprawić ten problem należy przesłać funkcji OnDraw () w miejsce prostokąta okna prostokąt
drukarki. Szczęśliwie szkielet aplikacji przyzywa wirtualną funkcję, którą możemy pokryć w widoku
wpisując tam odpowiednie informacje. Jak wspomnieliśmy wcześniej, funkcja ta nosi nazwę OnPrint () i
jest bardzo podobna do funkcji OnDraw (). Kiedy program odmalowuje zawartość okna przywoływana
jest funkcja OnDraw (), a gdy zawartość okna jest drukowana, przyzywana jest funkcja Onprint ().
Zastanawiające jest w jaki sposób funkcja OnDraw () wykorzystywana jest do wyświetlania podglądu
wydruku. Standardowa implementacja funkcji Onprint () w klasie CView przywołuje po prostu funkcję
OnDraw (), przesyłając jej kontekst urządzenia drukarki.
Funkcje wirtualne i polimorfizm
Funkcja wirtualna to funkcja zdefiniowana w klasie bazowej zawierająca często kod wykonujący
pewne standardowe operacje. Wewnątrz klasy wywodzącej się z klasy bazowej możemy zdefiniować
własną wersję tej funkcji, wykonującą oprócz standardowych operacji również zlecone przez nas
zadania. Zawsze gdy przywoływana jest funkcja o danej nazwie (nawet w obiekcie klasy bazowej),
przyzywana też jest funkcja z klasy pochodnej (ang. inherited). Technika ta nosi nazwę pokrywania
funkcji wirtualnych (ang. virtual function ovemding). Funkcja pokrywająca (ang. overri-de) może
również odwołać się do swojej wersji z klasy bazowej bezpośrednio przywołując pokrywaną (ang.
ovemden) funkcję, podając jako jej zakres kompetencji (ang. scope) klasę bazową. W ten sposób
funkcje pokrywające mogą korzystać z możliwości funkcji klasy bazowej. Wirtualne funkcje są w
języku C++ podstawowym sposobem implementacji polimorfizmu (ang. polymorphism).
Drukowanie i podgląd wydruku 569
Funkcja OnPrint () nie musi przyzywać funkcji OnDrawO . Możemy pokryć funkcję OnPrint (), aby
wydrukować zupełnie inny rysunek - większość aplikacji stara się jednak drukować dokładnie to co
widzi użytkownik. W tym celu wykorzystują po prostu funkcję OnDraw () przesyłając jej kontekst
urządzenia drukarki.
Pokrywanie wirtualnej funkcji OnPrint ( )
1. W oknie ProjectWorkspace kliknij zakładkę GlassYicw.
2. Kliknij znak plus u góry, aby otworzyć zestawienie klas projektu.
3. Aby wyświetlić odpowiednie menu skrótów, kliknij prawym klawiszem myszy klasę. do której chcesz
dodać pokrywającą funkcję OnPrint () (tutaj CPrintltView).
4. Wybierz polecenie Add Virtual Function, aby wyświetlić okno dialogowe New Vii tuał Ovemde.
5. Na liście New Virtual Functions znajdź wirtualną funkcję OnPrint.
6. Aby rozpocząć edycję funkcji OnPrintO, wciśnij przycisk Add and I (Sis Standardowa funkcja
pokrywająca OnPrint () wygląda tak:
void CPrintltView: :OnPrint (CDC* pO:.', CPn nt int ri* pl"r-', {
// TODO: Tutaj dodaj własny kcd
CView::OnPrint(pDC, pinfo);
}
Najbardziej rzucającą się w oczy różnicą między tą funkcją a funkcją OnDraw o JCS! wskaźnik
pinfo do obiektu cprintinfo. W obiekcie tym przechowywane są niezbędne informacje na temat
bieżącego wydruku, przede wszystkim potrzebne nam wymiary pro stokąta kontekstu urządzenia
drukarki. Klasa cprintinfo posiada wiele bardzo nck.i-wych zmiennych składowych. Niektóre z nich
przedstawione zostały w tabeli 22.1
Standardowa funkcja OnPrint () klasy CView
Standardowa funkcja pokrywająca tworzona przez kreatora przyzywa funkcję On-Print () klasy
bazowej CView. Jeśli zajrzymy do kodu źródłowego funkcji znajdującego się w module źródłowym . .
\MFC\SRC\VIEWCORE.CPP w katalogu języka Vi-suał C++, zobaczymy, w jaki sposób działa
automatyczna obsługa drukowania. Funkcja CView:: OnPrint () przywołuje po prostu funkcję OnDraw
() przesyłając jej wskaźnik kontekstu urządzenia pDC.
570_____________________________________Poznaj Visual C++ 6
Tabela 22.1. Niektóre zmienne składowe klasy CPrintinfo
Nazwa zmiennej Opis zawartości
m_nCurPage Bieżąca strona w wydruku wielostronicowym
m_nNumPreviewPages l lub 2 - w zależności od liczby wyświetlonych w podglądzie wydruku stron
m_rectDraw Rozmiary prostokąta drukowanej strony
m_pPD Wskaźnik do klasy CPrintDialog, jeśli korzystamy z okna dialogowego Print
m_bDirect Wartość TRUE, jeśli okno dialogowe Print ma zostać pominięte in_bPreview
Wartość TRUE, jeśli znajdujemy się w podglądzie wydruku m_strPageDesc Sformatowany łańcuch
pomagający generować numer strony m_lpUserData Wskaźnik do obiektu przechowującego dane
użytkownika
Niektóre z pozostałych zmiennych składowych klasy CPrintinfo opisane zostaną jeszcze w dalszej
części rozdziału, na początek potrzebne są nam jednak wymiary prostokąta wydruku. Zmienna
m_rectDraw przechowuje rozmiary prostokąta wydruku bieżącej drukowanej strony. Możemy
wykorzystać je w kontekście urządzenia wewnątrz funkcji OnDraw (). Struktury tej nie możemy przesłać
do funkcji OnDraw (), możemy jednak przekopiować odpowiednie wymiary prostokąta (ściślej,
współrzędne prawego dolnego rogu, które można traktować jak wymiary, ponieważ lewy górny róg ma
współrzędne O*, 0) do zmiennej składowej przechowywanej w klasie cprintview.
W tym celu po komentarzu // TODO, ale przed odwołaniem do funkcji cview: :0n-Print () należy
wpisać następujący fragment kodu:
// ** Kopiuj rozmiary prostokąta wydruku z struktury pinfo if (pinfo) m rcPrintRect = plnfo->m
rectDraw;
W ten sposób zachowamy wymiary prostokąta wydruku w zmiennej składowej m_rcPrintRect klasy
CPrintltView. Musimy teraz zadeklarować tę zmienna składową klikając prawym klawiszem myszy w
panelu ClassView okna ProjectWorkspace klasę CPrintltView i wybierając w menu skrótów polecenie
Add Member yariable. Typ zmiennej wpisywany w polu Variable Type to oczywiście CRect a jej
deklaracja to m_rcPrintRect. Zmienna powinna zostać zdefiniowana jako prywatna, żeby ukryć ją przed
innymi klasami.
' przyp. tłum.
Drukowanie i podgląd wydruku 57 l
Publiczne, prywatne i chronione
Każda funkcja i zmienna składowa zadeklarowana w definicji klasy może zostać oznaczona jako
publiczna, prywatna lub chroniona. Definiujemy w ten sposób, jak dana zmienna lub funkcja jest
dostępna dla funkcji innych klas. Zmienna lub funkcja publiczna dostępna jest dla wszystkich funkcji,
niezależnie do jakiej klasy należą. Zmienna lub funkcja prywatna dostępna jest tylko dla zmiennych
składowych tej samej klasy. Zmienna lub funkcja chroniona dostępna jest również tylko dla funkcji
składowych tej samej klasy oraz dodatkowo dla tych funkcji składowych klas z niej wywiedzionych,
które zostały zadeklarowane jako publiczne.
W języku C++ możemy w ten sposób zwiększyć bezpieczeństwo kodu programu, pozwalając na
sięganie do obiektów tylko za pomocą specjalnych (deklarowanych jako publiczne) funkcji dostępu.
Dzięki temu znacznie zmniejszamy ryzyko niepożądanych modyfikacji zmiennych i usuwamy z
programu jedno z potencjalnych źródeł błędów.
PATRZ TAKŻE
O klasach widoków i klasie cview pisaliśmy w rozdziale 12.
Kontekst urządzenia drukarki
Kontekst urządzenia, z którego korzysta funkcja onprint () różni się trochę od kontekstu urządzenia
ekranu. Prawdopodobnie będzie mieć mniej kolorów i będzie większy niż kontekst oferowany przez
ekran. Pozostałe atrybuty można wykorzystywać do rysowania dokładnie w taki sam sposób jak w
kontekście urządzenia ekranu.
Właśnie dzięki tym podobieństwom możemy wykorzystywać funkcję OnDraw () zarówno do
drukowania, jak i do rysowania w widoku, w taki sam sposób jak w przypadku funkcji cview:: OnPrint ()
klasy bazowej.
Kontekst urządzenia przechowuje znacznik, którego wartość można sprawdzić za pomocą funkcji
isPrinting (). Znacznik ten pozwala ustalić, czy korzystamy z kontekstu urządzenia ekranu, czy kontekstu
urządzenia drukarki. Wiedząc, z którego kontekstu urządzenia korzystamy, będziemy mogli przekształcić
obraz wyświetlany na ekranie w obraz drukowany lub po prostu zmienić odpowiednio rozmiary
wyświetlanego obrazu, tak aby nadawał się do wydruku.
W tworzonym programie pozostaje nam tylko wykorzystać współrzędne zapisane w zmiennej
m_PrintRect w funkcji OnDraw (), wtedy gdy funkcja ta jest wykorzystywana do drukowania. Konieczne
jest w tym przypadku skorzystanie z funkcji isprinting (), aby ustalić, czy w danym momencie funkcja
OnDraw () powinna korzystać z prostokąta obszaru roboczego okna, czy z prostokąta drukarki. Funkcja
OnDraw () przedstawiona została na listingu 22.2, a jej efekt na rysunku 22.2.
572___________________________________Poznaj Visual C++ 6
Listing 22.2. LST23_2.CPP - funkcja OnDrawf) sprawdzająca, czy wykorzystujemy ją do rysowania na
ekranie, czy do drukowania
1 // Zadeklaruj obszar roboczy
2 CRect rcCIient;
3
4 // ** Sprawdź, czy kontekst urządzenia jest kontekstem drukarki
5 if (pDC->IsPrinting())
6 (
7 // ** Jeśli tak, użyj prostokąta obszaru wydruku
8 rcCIient = m_rcPrintRect;
9 }
10 else
11 (
12 // ** Jeśli nie, skorzystaj z prostokąta obszaru roboczego
13 GetCIientRect(SrcCIient) ;
14 }
15
16 // P.rzeksztaić w jednostki logiczne
17 pPC->DPtoLP(&rcClient); O__________________________________
O Tutaj zmieniamy współrzędne wyrażone w pikselach na współrzędne w jednostkach
logicznych równych 1/10 milimetra.
W linii 5 w instrukcji if przyzywana jest funkcja isPrinting (). Funkcja ta zwraca wartość
TRUE, jeśli kontekst urządzenia jest kontekstem drukarki (lub podglądu wydruku), a FALSE, gdy
jest to jakikolwiek inny kontekst urządzenia. Jeśli okaże się, że funkcja wykorzystywana jest do
drukowania, przypisujemy (w linii 8) prostokąt drukowanej strony zmiennej rcCIient. Jeśli nie,
pobieramy prostokąt obszaru roboczego okna za pomocą funkcji GetCIientRect () (linia 13).
Ponieważ korzystamy z trybu odwzorowywania, musimy przekształcić zarówno wymiary
prostokąta wydruku, jak i prostokąta rysunku wyświetlanego na ekranie w jednostki logiczne.
Zajmuje się tym w linii 17 funkcja DPtoLP (). Po dodaniu kodu z linii 4-14 do poprzedniej
wersji funkcji OnDraw (), a następnie zbudowaniu i uruchomieniu aplikacji, podgląd wydruku
przedstawiany przez program będzie znacznie lepszy (rysunek 22.2).
Dmkowanie i podgląd wydruku 573
Pagel ^Kil?!,'1.^: ! i^.i.a^J:.':1:'^;,,^:^;''!!'':';^?-1 :a;BSSf
Rysunek 22.2. Podgląd wydruku utworzony w oparciu o wymiary prostokąta strony wydruku.
PATRZ RÓWNIEŻ
Szczegółowy opis trybów odwzorowywania można znaleźć w rozdziale 15.
Odpowiedni stosunek długości do szerokości strony
Ponieważ kartka papieru w drukarce jest zazwyczaj długa i wąska, przedstawiany w oknie podgląd
wydruku zostaje odpowiednio ściśnięty. Stosunek długości do szerokości (ang. aspect ratio) jest bardzo
ważny przy drukowaniu obrazu z ekranu. Aby zapobiec niepożądanemu rozciąganiu się rysunku w jedną
bądź w drugą stronę przy drukowaniu, musimy zadbać, aby stosunek ten nie zmieniał się po drodze od
monitora do drukarki. Funkcja przedstawiona na listingu 22.2 nie zajmowała się tym problemem. Jednak
dobrze działający program powinien zadbać o poprawność wydruku także i w tym zakresie.
Najlepszą strategią jest w tym przypadku sprawdzenie, które rozwiązanie pozwoli na wyświetlenie
większego rysunku: czy dopasowanie jego szerokości do szerokości kartki, czy wysokości do wysokości
kartki, a następnie takie skrócenie drugiego z wymiarów, aby stosunek długości do szerokości pozostał
niezmieniony.
Problemy z niektórymi drukarkami
Większość drukarek drukuje zachowując proporcje długości do szerokości piksela takie same jak na
ekranie (1:1). Jeśli jednak przyjrzymy się drukarkom termicznym, na przykład w faksach, zauważymy,
że potrafią one niekiedy znacznie zmieniać stosunek długości do szerokości drukowanych pikseli.
574_____________________________________Poznaj Visual C++ 6
W tym celu potrzebne nam będą informacje o rozmiarach papieru i stosunku długości do szerokości
strony używanej w drukarce. Do zdobywania tych (jak również innych) informacji służy funkcja
GetDeviceCaps () kontekstu urządzenia. Przesyłając funkcji GetDeviceCaps () znacznik ASPECTX lub
ASPECTY możemy zdobyć informacje o stosunku długości do szerokości drukowanych pikseli. Jeśli
stosunek ten wynosi 1:1, piksel jest kwadratowy; w przeciwnym wypadku jest wydłużony i po
wydrukowaniu rysunek może mieć inne proporcje niż na ekranie. W takiej sytuacji należy zdecydować,
wzdłuż której osi dopasowanie da na wydruku większy rysunek, oczywiście przy zachowaniu tych sa-
mych proporcji długości do szerokości co na ekranie. W ten sposób unikniemy ewentualnych
generowanych przez drukarkę zniekształceń rysunku.
Kod odpowiednio zmodyfikowanej w tym celu funkcji OnDraw () przedstawiony został na listingu
22.3.
Listing 22.3. LST23_3.CPP - zachowywanie tych samych proporcji drukowanego rysunku co na ekranie
1 //** Zadeklaruj i pobierz prostokąt obszaru roboczego
2 CRect rcCIient;
3 GetClientRect(&rcClient);
4
5 // ** Sprawdź, czy używamy kontekstu urządzenia drukarki B if (pDC->IsPrinting())
7 {
8 // ** Ustal proporcję szerokość wydruku .'szerokość okna
9 double dWidthRatio=(double)m_rcPrintRect.Width()/
10 (double)rcCIient.WidthO;
11
12 // ** Ustal proporcję wysokość wydruku:wysokość okna
13 double dHeightRatio=(double)m_rcPrintRect.Height()/ O
14 (double)rcCIient.Height();
15
16 // ** Wylicz proporcje długości do szerokości dla drukarki
17 double dAspect=(double)pDC->GetDeviceCaps(ASPECTX)/ @
18 (double)pDC->GetDeviceCaps(ASPECTY) ;
19
20 // ** Wylicz nową, dostosowaną proporcjonalnie wysokość
21 int nHeight=(int)(rcCIient.HeightO *
22 dWidthRatio * dAspect ) ;
23
24 // ** Wylicz nową, dostosowaną proporcjonalnie szerokość
25 int nWidth=(int) (rcCIient.WidthO *
26 dHeightRatio * (1.0 / dAspect) );
27
Dmkowanie i podgląd wydruku 575
28 // ** Zdefiniuj rozmiary prostokąta wydruku
29 rcClient=m_rcPrintRect;
30
31 // ** Sprawdź jak lepiej dopasować rysunek do strony
32 if (nHeight > nWidth)
33 (
34 // ** Jeśli najlepiej wzdłuż, dostosuj szerokość
35 rcCIient.BottomRight().x=
36 m_rcPrintRect.TopLeft().x + nWidth;
37 }
38 else
39 (
40 // ** Jeśli najlepiej wszerz, dostosuj wysokość
41 rcCIient.BottomRight() .y=
42 m_rcPrintRect.TopLeft().y + nHeight;
43 }
44 }
45
46 // Przekształć na jednostki logiczne
47 pDC->DPtoLP(SrcCIient);
O Ustalamy różnicę w stosunku długości do szerokości dla strony wyświetlanej na ekranie i strony
drukowanej przez drukarkę.
@ W oparciu o informacje uzyskane za pomocą kontekstu urządzenia wyliczamy różnice wynikające
ze sposobu drukowania pikseli przez drukarkę.
W zależności od tego, w którym kierunku dopasowanie da większy rysunek, dostosowujemy jego
wymiary.
Warto zauważyć, że zarówno gdy rysunek jest drukowany, jak i gdy jest wyświetlany na ekranie,
wykorzystujemy wymiary okna uzyskane w linii 3 za pomocą funkcji GetC-lientRect (). Jeśli rysunek
ma zostać wyświetlony na ekranie, nie musimy go dalej dopasowywać i jest on rysowany tak jak w
pierwszym przykładzie.
Jeśli jednak funkcja isPrinting () przyzywana w linii 6 zwróci wartość TRUE, oznacza to, że rysunek
jest drukowany i należy wykonać seńę dostosowań. Najpierw musimy ustalić stosunek długości okna do
długości kartki i szerokości okna do szerokości kartki. Znajdujemy je odpowiednio w liniach 9 i 13
dzieląc wymiary kartki przez wymiary okna.
Następnie trzeba wyliczyć różnice wynikające ze sposobu drukowania pikseli. W linii 17
przyzywamy funkcję GetDeviceCaps (), a ustalony stosunek długości do szerokości drukowanego
piksela przechowujemy w zmiennej dAspect.
576 Poznaj Visual C++ 6
W oparciu o te wartości możemy teraz wyliczyć relatywną szerokość i wysokość po
wprowadzeniu poprawek wynikających z różnic w rozmiarach prostokąta obszaru roboczego i
sposobu przedstawiania pikseli (odpowiednio w liniach 21 i 25). W oparciu o te wartości
będziemy mogli bez obawy zniekształceń dostosować wymiary rysunku do wymiarów strony.
Najpierw jednak musimy zdecydować, czy będziemy dopasowywać rysunek wzdłuż, czy
wszerz. Wyrażenie warunkowe w linii 32 sprawdza, w którym przypadku rysunek będzie
większy. Oznacza to, że jeśli rysunek w oknie był wysoki i wąski, najlepiej dopasować do
rozmiarów kartki wysokość i odpowiednio do zmian wysokości dostosować szerokość. W
zależności od tego, czy korzystniejsze jest dopasowanie w poziomie, czy w pionie, w linii 35
lub w linii 42 odpowiednio dostosowywana jest współrzędna x lub y prawego dolnego rogu
rysunku.
Niektóre powszechnie spotykane opcje drukowania
Aplikacje graficzne udostępniają zazwyczaj kilka dodatkowych opcji pozwalających na zmienianie
sposobu dostosowywania pionowego i poziomego wymiaru drukowanego rysunku. Opcja, którą tutaj
dołączyliśmy, powszechnie znana jest pod nazwą Best Fit. Inną powszechnie stosowaną jest opcja Size
to Page, dopasowująca wymiary rysunku do wymiarów strony (bez zachowania proporcji między
wysokością a szerokością). Czasami można również natknąć się na opcję Original Image Size,
drukującą (zazwyczaj zorientowany centralnie na stronie) rysunek z zachowaniem proporcji,
rozmiarów rysunku widocznego na ekranie (z uwagi na dość duże rozdzielczości drukarek, rysunek
wychodzi wtedy raczej mały).
W linii 29 prostokątowi obszaru roboczego przypisywane są wymiary prostokąta wydruku,
a następnie rysunek jest dopasowywany do rozmiarów kartki, tak aby był możliwie jak
największy. Po tym ostatnim dopasowaniu program przystępuje do rysowania.
Tak zmodyfikowana aplikacja będzie drukować lub wyświetlać podgląd wydruku rysunku,
zachowując te same proporcje długości do szerokości co na ekranie. Jeśli rozciągniemy okno
w pionie, wydruk będzie dostosowywać się do wysokości kartki, modyfikując szerokość tak,
aby ich wzajemne proporcje pozostały zachowane.
PATRZ TAKŻE
Więcej informacji na temat możliwości różnych urządzeń można znaleźć w rozdziale 15.
Stronicowanie i orientowanie wydruku
Bardzo często aplikacja zmuszona jest obsługiwać nie tylko wydruk pojedynczej strony,
ale również druk długich i często skomplikowanej konstrukcji dokumentów. Szkielet aplikacji
także i w tym przypadku przychodzi nam z pomocą, dostarczając okna dialogowego Print
Setup i systemu pozwalającego drukować i oglądać określoną liczbę stron.
Drukowanie i podgląd wydmku _____ 577
Drukowanie pojedynczego rysunku na więcej niż jednej stronie s;;5 ^
Może się zdarzyć, że będziemy musieli wydrukować pojedynczy duży rysunek podzielony
na c?ęsd na więcej niż jednej stronie. W Tym celu trzeba będzie w kontekście urządzenia,
podzielić rysunek. Tutaj przydaje się klasa CScroliYiew, która dostarcza ułatwiającej
dzielenie funkcji ScrollTo?osit.'on{).i pozwala na stosunkowo swobodne skalowanie
rysunku. Niemniej nadaj będziemy musieli ręcznie obsługiwać przenoszenie kontekstu
urządzenia między poszczególnymi stronami.
Definiowanie pierwszej i ostatniej strony
Podstawową sprawą przy drukowaniu wielostronicowych dokumentów jest zdefiniowanie
pierwszej i ostatniej strony wydruku, co ustali również, ile stron ma zostać wydrukowanych.
Kiedy rozpoczyna się drukowanie, wzywana jest wirtualna funkcja szkieletu aplikacji. Funkcja
ta nosi nazwę OnPreparePrintingO i posiada tylko jeden parametr, obiekt pinfo klasy
cprintinfo. Tutaj po raz pierwszy pojawia się w programie obiekt cprintinfp i tu po raz
pierwszy mamy okazję dostosować go do naszych potrzeb. Funkcja OnPreparePrintingO jest
przez kreator AppWizard automatycznie dołączana do aplikacji SDI, dzięki czemu nie musimy
robić tego sami. Kod funkcji możemy obejrzeć klikając dwukrotnie w panelu ClassView
funkcję składową OnPreparePrinting () klasy CPrintView.
Kod funkcji powinien wyglądać mniej więcej tak:
BOOL CPrintItView::OnPreparePrinting(CPrintInfo* pinfo)
(
// standardowe przygotowania do drukowania
return DoPreparePrinting (pinfo) ;
}
Domyślnie przyzywana jest tu funkcja DoPreparePrinting (), której jako parametr
przesyłany jest wskaźnik pinfo do obiektu CPrintInfo. Funkcja DoPreparePrinting () definiuje
odpowiedni kontekst urządzenia i jeśli drukujemy (a nie tylko oglądamy podgląd wydruku),
przyzywa standardowe okno dialogowe Print (Drukuj). Okno to opiszemy dokładniej dalej.
Zanim jednak zawezwiemy funkcję DoPreparePrinting (), musimy zdefiniować zakres
drukowanych stron modyfikując odpowiednio obiekt cprintinfo.
W tym celu przed komentarzem// standardowe przygotowania do drukowania należy
wpisać:
p!nfo->SetMinPage(2) ;
pInfo->SetMaxPage(8) ;
Przedstawione tutaj funkcje składowe klasy cprintinfo modyfikują obiekt CPrintln-fo przesyłany za
pomocą wskaźnika pinfo, tak aby drukowanie rozpoczynało się od strony drugiej (SerMinPage ()), a
kończyło się na stronie 8 (SetMaxPage ()).
578___________________________________Poznaj Visual C++ 6
W ten sposób w trakcie drukowania dokumentu funkcja OnPrint () jest przyzywana sześć
razy. Za każdym razem przesyłana jest jej zmienna składowa pinfo->m_nCurPage podająca
bieżącą drukowaną stronę.
W zależności od aplikacji, sposób definiowania liczby stron przeznaczonych do druku
będzie się zmieniać. Jeśli przykładowo sprzedajemy płyty kompaktowe i chcemy wydrukować
katalog oferowanych przez nas płyt, powinniśmy każdemu z krążków poświęcić jedną stronę.
Jeśli więc mamy w ofercie 120 płyt, będziemy musieli wydrukować 120-stronicową broszurę.
Jeśli natomiast tworzymy rozbudowane sprawozdanie lub ofertę biznesową zawierającą liczne
dane liczbowe i wykresy, liczbę stron i wysokość poszczególnych elementów graficznych
będziemy w programie ustalać dopiero po dokonaniu odpowiedniego ręcznego składu tekstu.
Niezależnie od tego, kiedy ustalimy liczbę stron, gdy zostanie już ona zdefiniowana, musimy
ją w funkcji OnPreparePrintO przypisać obiektowi CPrintInfo.
Omijanie okna dialogowego Print
Czasami nie ma potrzeby komplikowania użytkownikowi życia i wyświetlania okna
dialogowego Print; możemy je pominąć przypisując w funkcji. OnPrepareprin tino()
zmiennej pInfo-s.in__bDirect wartość TRUE. ; ,','"::'
Aby pokazać różnicę między drukowaniem zawartości okna a drukowaniem wielostronicowego
dokumentu, możemy (co zostało pokazane na listingu 22.4) zamieścić w funkcji OnPrint () osobny kod
odpowiedzialny za drukowanie w tym drugim przypadku. W tej wersji funkcji OnPrint () funkcja klasy
bazowej cview:: OnPrint nie jest w ogóle wzywana, co oznacza również, że nie jest wykonywana funkcja
OnDraw (). W tym przypadku obraz wydrukowany i obraz widoczny w oknie są zupełnie inne.
Listing 22.4. LST23_4.CPP - zmienianie wydruku w zależności od drukowanej strony w funkcji OnPrint
()
1 void CPrintItView::OnPrint(CDC* pDC, CPrintInfo* pinfo)
2 {
3 // TODO: Tutaj dodaj własny kod
4
5 // ** Utwórz i wybierz czcionkę
6 CFont fnTimes;
7 fnTimes.CreatePointFont(720,"Times New Roman",pDC); O
8 CFont* p01dFont=(CFont*)pDC->SelectObject(SfnTimes);
9
10 // ** Utwórz i wybierz pędzel
11 CBrush brHatch(HS_CROSS,RGB(64,64,64) ) ;
12 CBrush* pOldBrush =
Drukowanie i podgląd wydruku 579
(CBrush*)pDC->SelectObj ect(SbrHatch) ;
// ** Utwórz tekst strony CString strDocText;
strDocText.Format("Page Number %d",
plnfo-> m__nCurPage) ;
pDC->SetTextAlign(TA_CENTER+TA_BASELINE) ;
// ** Zdefiniuj kilka pomocniczych punktów CPoint ptCenter=pInfo-
>m_rectDraw.CenterPoint() ;
CPoint ptTopLeft-pInfo^n^rectDraw.TopLeft ();
CPoint ptBotRightpInfo->m_rectDraw. BottomRight () ;
// ** Zdefiniuj rogi rombu
CPoint ptPolyArray[4)=
(
CPoint(ptTopLeft.x,ptCenter.y), .
CPoint(ptCenter.x,ptTopLeft.y),
CPoint(ptBotRight.x,ptCenter.y),
CPoint(ptCenter.x,ptBotRight.y)
};
// ** Narysuj romb pDC->Polygon(ptPołyArray,4) ;
// ** Namaluj tekst pDC->TextOut(ptCenter.x,ptCenter.y,strDocText); O
// ** Przywróć poprzedni pędzel l czcionkę pDC-
>SelectObject(pOldFont) ;
pDC->SelectObject(pOldBrush) ;
45 }
O Za pomocą funkcji CreatePointFont () definiujemy czcionkę o rozmiarach 72 punktów.
@ Z obiektu cprintinfo pobieramy numer bieżącej drukowanej strony.
Definiujemy rogi rombu.
O Drukujemy tekst zawierający numer bieżącej drukowanej strony.
580_____________________________________Poznaj Visual C++ 6
W liniach 6-12 definiowane są odpowiednie zasoby wykorzystywane do tworzenia
drukowanego rysunku (czcionka i pędzel). Lepszym jednak miejscem na definiowanie
tych zasobów jest opisana dalej w tym rozdziale funkcja OnBeginPrinting ().
W linii 17 pokazaliśmy jak można modyfikować drukowany tekst w zależności od te-
go, którą stronę właśnie drukujemy. W prawdziwej aplikacji zmienną m_nCurPage
można wykorzystać do sięgania i operowania na kolejnych stronach dokumentu. W
przykładzie z katalogiem płyt kompaktowych numer strony może być wykorzystywany
do odwoływania się do odpowiedniego krążka, na przykład aby wydrukować razem z
poświęconym mu tekstem odpowiednią okładkę. Nie mamy tutaj niestety miejsca, by
przedstawić tak skomplikowany przykład, dlatego ograniczyliśmy się do wykorzystania
zmiennej pinfo->m_curpage do drukowania numeru strony.
W liniach 22-37 definiujemy kratkowany romb, który stanowić będzie tło tekstu. W
linii 40 drukujemy pośrodku strony tekst podający numer strony, a w liniach 43 i 44
przywracamy dawną czcionkę i pędzel.
W jaki sposób biblioteka MFC obsługuje podgląd wydruku?
Podgląd wydruku obsługiwany jesi za pomocą specjalnej, nic opisanej w dokumentacji klasy
CPreviewDC. Ten specjalny kontekst urządzenia działa tak samo jak kontekst urządzenia
drukarki, ale wyświetla obraz na ekranie tak jak kontekst urządzenia ekranu. To nietypowe
zachowanie kontekstu urządzenia konieczne jest, by można było na ekranie przedstawiać
pewne typowe dla drukarki funkcje, takie jak na przykład stronicowanie. Mimo iż klasa
CPreviewDC nie została opisana w dokumentacji, możemy zapoznać się z nią przeglądając
jej kod źródłowy, który znaleźć można w katalogu języka Visual C++ w module źródłowym
MFC\SRC\DCPREV.C.PP.
Po uruchomieniu zbudowanej aplikacji będziemy mogli w podglądzie wydruku (menu
File, polecenie Print Pre^iew) przeglądać kolejne strony drukowanego dokumentu za pomocą
przycisków Next Page i PIW Page (rysunek 22.3). Jeśli mamy zainstalowaną drukarkę,
możemy również dokument wydrukować.
PATRZ TAKŻE
Jak wybierać obiekty w kontekście urządzenia pisaliśmy w rozdziale 15.
Malowanie za pomocą różnych rodzajów pędzli opisane zostało w rozdziale 16.
Drukowanie i podgląd wydruku 581
Rysunek 22.3. Podgląd wydruku wielostronicowego dokumentu
Okno dialogowe Print
Kiedy drukujemy wielostronicowy dokument, program przedstawia nam (pokazane na
rysunku 22.4) okno dialogowe pozwalające ustalić co dokładnie chcemy wydrukować. Jest to
standardowe okno dialogowe Print, wywoływane na ekran przez funkcję cview:: Do-
PreparePrinting (), która Z kolei przyzywana jest przez funkcję pokrywającą OnPrepare-
Printing (). W oknie tym możemy zdefiniować, które strony mają zostać wydrukowane, liczbę
kopii, znaczniki sortowania (opcja Cfillate), drukarkę, na której chcemy drukować i
szczegółowe właściwości drukarki.
Poić wyboru CgHate ' , .. , , : ' ,-',"'
SsSIl drukując .kilka kopii dokuroentu użytkownik zaznaczy w oknie Print pole wyboru
CoKate, sterowmk drukarki będzie drukował strony kompletami - strony o tym samym
numerze ze wszystkich kopii dokumentu razem. Opcja ta dołączana jest automatycznie i nie
musimy jej ręcznie programować, musi być jednak obsługiwana przez sterownik drukarki.
Jeśli drukarka nie pozwala na Taki sposób drukowania, opcja będzie w oknie dialogowym
wyświetlana jako nieaktywna.
Użytkownik może w tym oknie dialogowym zmienić opcje drukowania, co spowoduje
zmianę odpowiednich ustawień w obiekcie CPrintinfo zanim zostanie on przesłany do
aplikacji. Okno to możemy zmodyfikować w tworzonym programie w zależności od licz-
by opcji, które chcemy użytkownikowi udostępnić.
582 Poznaj Visual C++ 6
Rysunek 22.4. Standardowe okno dialogowe Print
W tabeli 22.1 prezentującej zmienne składowe klasy CPrintinfo znajdował się wskaźnik
m_pPD. Wskaźnik ten jest odwołaniem do klasy cprintDialog obudowującej w bibliotece MFC
okno dialogowe Print. Klasa ta posiada zmienną składową m_pd, czyli strukturę PRINTDLG
przechowującą domyślne ustawienia wyświetlane w oknie dialogowym Print. Jak widać z
listingu 22.5, struktura ta posiada wiele zmiennych składowych. Zmienne te pozwalają, daleko
idące dostosowanie domyślnych ustawień okna dialogowego, nawet tak daleko (jeśli lubimy
wyzwania), żeby zastąpić domyślny szablon okna dialogowego zupełnie innym szablonem.
Nie mamy tutaj możliwości szczegółowego opisania wszystkich zmiennych składowych tej
struktury, niemniej jedną z bardziej oczywistych jest zmienna nCopies. Jeśli przed wezwaniem
funkcji CView: :DoPreparePrinting() zmienimy wartość przypisaną tej zmiennej, zmieni się
domyślnie wyświetlana w oknie liczba kopii wydruku. Możemy to zrobić wpisując w funkcji
OnPreparePrinting ():
pInfo->m_pPD->m_pd.nCopies = 15;
,v ' / . - ', .:: "'.. ' .
'.
'Korzystanie ze struktury DeyMode
Struktura DevMode przechowuje wiele przydatnych parametrów definiujących techniczne
możliwości i konfigurację urządzenia. Wskaźnik tej struktury można pobrać
za-pomocą funkcji GetDevMode() klasy CPrintDialog.
Jeśli po dodaniu tej linii otworzymy okno dialogowe Print, domyślna liczba kopii wy-
świetlana w oknie zmieni się na 15 (oczywiście jeśli zainstalowana drukarka pozwala na
drukowanie kilku kopii dokumentu). Podobnie zmieniać można inne wartości zapisane w
strukturze PRINTDLG.
583
Drukowanie i podgląd wydruku_________
Listing 22.5. LST23_5.CPP- struktura PRINTDLG
1 typedef struct tagPD (
2 DWORD IStructSize;
3 HWND hwndOwner;
4 HANDLE hDevMode;
5 HANDLE hDevNames;
6 HDC hDC;
7 DWORD Flags;
8 WORD nFromPage ;
9 WORD nToPage;
10 WORD nMinPage;
11 WORD nMaxPage;
12 WORD nCcpies;
13 HINSTANCE hinstance;
14 DWORD ICustData;
15 LPPRINTHOOKPROC IpfnPrintHook; O
16 LPSETUPHOOKPROC IpfnSetupHook;
17 LPGTSTR IpPrintTemplateName;
18 LPCTSTR IpSetupTemplateName;
19 HANDLE hPrintTemplate;
20 HANDLE hSetupTemplate;
21 ) PRINTDLG;
.-Ostatnie, sześć 'zmiennych'pozwala dostosować' c'kho'dialogowej do' naszych potrzeb),'
umożliwiając zdefiniowanie własnego :szabSo!.iu ok-na dialogowego.. Jest to : dość
skomplikowane zagądmettie, zmienrse. te pozw!Uają'jedrtak',dowplQie modyfikować wygląd
interfejsu, któły^zedaawiamyui^tkowhikowfceS^a:^:^!^^^-;';!?:^
Po tym jak użytkownik wciśnie w oknie dialogowym przycisk OK, wprowadzone przez
niego zmiany można pobrać za pomocą przedstawionych w tabeli 22.2 funkcji dostępu klasy
CPrintDialog. Jeśli na przykład chcemy przed rozpoczęciem drukowania ustalić, ile kopii
wydruku użytkownik sobie zażyczył, możemy za pomocą odpowiedniej funkcji przechwycić
liczbę kopii zwróconą z okna dialogowego przez funkcję cview:: DoPreparePrinting () (tak jak
na listingu 22.6).
Oczywiście w podobny sposób można sprawdzić każdą inną wartość zapisaną w struk-
turze PRINTDLG.
584___________________________________Poznaj Visual C++ 6
Tabela 22.2. Funkcje dostępu klasy CPrintDialog
Funkcja Opis działania
GetCopies () Zwraca ustaloną przez użytkownika liczbę kopii GetFromPage ()
Podaje początkową stronę wydruku GetToPage () Podaje końcową stronę
wydruku GetPortName () Zwraca port drukarki, na przykład LPT1:
GetDriverName () Podaje sterownik wybranej drukarki (drukarki docelowej)
GetPrinterDC () Zwraca kontekst urządzenia drukarki
PrintAll () Zwraca wartość TRUE, jeśli drukowany ma być cały dokument
PrintCollate () Zwraca wartość TRUE, jeśli strony mają być sortowane
PrintRange () Zwraca wartość TRUE, jeśli zdefiniowany został określony zakres stron
PrintSelection () Zwraca wartość TRUE, jeśli wybrane zostały strony określonego typu
Listing 22.6: LST23_6.CPP zatwierdzanie wybranej przez użytkownika w oknie dialogowym liczby
kopii
1 BOOL CPrintItView::OnPreparePrinting(CPrintInfo* pinfo)
2 {
3 pInfo->SetMinPage(l) ;
4 pInfo->SetMaxPage(10) ;
5
6 pInfo->m_pPD->m_pd.nCopies = 3;
7
8 do
9 {
10 // ** Sprawdź, czy użytkownik nie anulował drukowania
11 if (DoPreparePrinting(pInfo) == FALSE) O
12 return FALSE;
13
14 // ** Ostrzeż użytkownika, jeśli zażyczył sobie zbyt wielu kopii
15 if (pInfo->m_pPD->GetCopies()>5)
16 AfxMessageBox("Please choose less than 5 copies") ;
17
18 // ** Powtarzaj pętlę, dopóki użytkownik nie zadeklaruje rozsądnej liczby kopii
19 } while(pInfo->m_pPD->GetCopies()>5); @
585
Drukowanie i podgląd wydruku
20 return TRUE;
21 }
O Wewnątrz pętli musimy pozwolić użytkownikowi przerwać operację drukowania za pomocą
przycisku Cancel, gdyby w którymś momencie zrezygnował z drukowania
Warunek tutaj zdefiniowany będzie powtarzał pętlę tak długo, jak długo użytkownik nie zdefiniuje
liczby kopii mniejszej lub równej pięciu. Przedstawiamy tutaj sposób w jaki można w programie
sprawdzać i zatwierdzać parametry wprowadzone przez użytkownika
Przyzywana w liniach 11 i 12 funkcja CYiew: :DoPreparePrinting() zwraca wartość FALSE, jeśli
użytkownik wciśnie przycisk Cancel. Jeśli nie, zdefiniowana przez użytkownika liczba kopii wydruku
jest sprawdzana w linii 15 i jeśli jest ich więcej niż pięć, użytkownik jest informowany, że powinien
ograniczyć liczbę kopii. Pętla jest powtarzana tak długo, dopóki użytkownik nie wprowadzi
odpowiedniej liczby kopii albo nie wciśnie przycisku Cancel.
Opcje orientacji strony
Jeśli w menu File wybierzemy polecenie Pnnt Setup (Ustawienia strony), będziemy mogli zmienić
orientację strony w drukarce. W oknie dialogowym, które się pojawi, dostępne są opcje Portrait i
Landscape (orientacje pionowa i pozioma). Aby móc drukować dokument w orientacji poziomej, nie
trzeba dołączać do programu żadnego dodatkowego kodu. Jeśli wybierzemy opcję Landscape i
uruchomimy podgląd wydruku, zobaczymy, że kontekst urządzenia został przekształcony tak, aby
wyświetlać stronę ułożoną poziomo. Tak długo jak długo aplikacja pobiera odpowiednie informacje na
temat rozmiaru strony ze zmiennej składowej rectDraw, obsługa drukowania w poziomie jest
automatyczna.
Różne rozmiary papieru
Użytkownik może również zdefiniować różne rozmiary papieru, poczynając od rozmiaru kopertowego,
a kończąc na stronach rozmiaru A3. Aby załączyć te opcje, nie musimy niczego programować
albowiem kontekst urządzenia zostanie automatycznie dopasowany do zdefiniowanych rozmiarów
strony. Zmiennej składowej obiektu cprintinfo, rectDraw zostaną przypisane odpowiednie współrzędne
(rozmiary) odpowiadające wymiarom kontekstu urządzenia.
586 Poznaj Visual C++ 6
Dodawanie obiektów GDI za pomocą funkcji OnBeginPrinting ()
Jak już mówiliśmy, funkcja przedstawiona na listingu 22.4 działa poprawnie, ale istnieje
lepszy sposób alokowania w pamięci zasobów potrzebnych do rysowania. Za każdym razem,
gdy strona jest drukowana, program wzywa funkcję Onprint (), która tworzy wszystkie
niezbędne zasoby (takie jak pędzel) od nowa. W przypadku dość prostego wydruku nie
spowolni to zbytnio programu, ale gdy drukowany rysunek lub tekst jest bardziej
skomplikowany, wygodniej jest zdefiniować zasoby i dokonać odpowiednich obliczeń tylko
raz, na początku procesu drukowania. W ten sposób korzystając z raz zdefiniowanego
kompletu zasobów będziemy mogli wydrukować dowolną liczbę kopii danej strony, a zasoby
zostaną usunięte z pamięci dopiero po zakończeniu drukowania.
Idealnym miejscem na zdefiniowanie tych zasobów jest wirtualna funkcja OnBegin-
Printing (), a jej siostrzana funkcja OnEndPrintingO jest najlepszym miejscem na ich
usunięcie. Funkcja OnBeginPrinting () jest przyzywana zaraz po funkcji OnPreparePrin-ting ().
W niej po raz pierwszy jest definiowany kontekst urządzenia. Tenże kontekst urządzenia
wykorzystywany będzie w trakcie drukowania, dlatego funkcja OnBeginPrinting () jest
właściwym miejscem, by zdefiniować wykorzystywane podczas drukowania obiekty GDI i
rozmiar strony. Kreator CIassWizard standardowo tworzy pustą funkcję:
void CPrintItView::OnBeginPrinting(CDC* /*pDC*/,
Ś CPrintInfo* /*plnfo*/)
(
// TODO: tutaj można dodać dodatkowy kod inicjujący drukowanie }
Przyjrzyjmy się bliżej definicji funkcji. Parametry są opatrzone komentarzami dla
kompilatora i jeśli któregoś z nich nie wykorzystamy, podczas kompilacji pojawi się ko-
munikat ostrzegawczy. Należy więc pamiętać o usunięciu tych komentarzy.
Możemy teraz dodać do funkcji odwołanie tworzące obiekt GDI, dzięki czemu nie bę-
dziemy musieli powtarzać tej procedury przy drukowaniu każdej kolejnej strony:
m_fnTimes.CreatePointFont(720,"Times New Roman",pDC);
m_brHatch.CreateHatchBrush(HS_CROSS,RGB(64,64,64));
Warto zauważyć, że obiekty fnTimes i brHatch poprzedzone są literą m_- w ten sposób oznaczamy
obiekty, których zakres kompetencji (ang. scope) rozciąga się na całą klasę, a nie tylko na daną funkcję.
Ponieważ będziemy sięgać do obiektów GDI w funkcji Onprint (), należy dodać je do deklaracji klasy w
następujący sposób:
protected:
CFont m fnTimes;
CBrush m brHatch;
Drukowanie i podgląd wydruku 587
Drukowanie w kolorach a drukarki jednobarwne
Wartość COLORREF zdefiniowana przez makroinstrukcję RGB(64, 64, 64) przypisuje
pędzlowi kolor ciemnoszary. Większość drukarek jednobarwnych stosunkowo dobrze oddaje
różne odcienie szarego odwołując się do techniki ditheringu (odwzorowywania kolorów za
pomocą mozaiki punktów). Korzystając z czarno-biatej drukarki można również zdefmiować
kolor nie będący odcieniem szarego. Drukarka postara się wtedy zastąpić go najbliższym mu
oferowanym przez siebie kolorem. Podczas, gdy drukarki kolorowe będą zapewne w stanie
całkiem dobrze oddać definiowany kolor, na drukarce czarno-białej otrzymamy po prostu
jakiś odcień szarości.
Obiekty te można dodać przez dwukrotne kuknięcie w oknie CIassYiew klasy CPrint-
View i bezpośrednie ich wpisanie, albo też za pomocą okna dialogowego Add Member
Yariable.
Warto również zauważyć, że do tworzenia kreskowanego pędzla wykorzystywany jest nie
kreator, ale funkcja CreateHatchBrushO. Dzieje się tak dlatego, że pędzel istnieć będzie tak
długo jak długo istnieć będzie widok. Należy więc pamiętać, aby w funkcji OnBeginPrintingO
zawezwać funkcję DeleteObject (), która usunie pędzel. Obiekty GDI czcionki i pędzla można
usunąć w funkcji OnEndPrinting () w następujący sposób:
m fnTimes.DeleteObject();
m_brHatch.DeleteObject() ;
Pozostało nam tylko do zrobienia usunięcie lokalnych obiektów GDI z funkcji on-Print () i
zastąpienie odwołań do nich ich wersjami ze zmiennych składowych. Robimy to zastępując
CFont i CBrush odpowiednio lokalnymi zmiennymi fnTimes i brHatch wraz z ich
odpowiednimi funkcjami kreacji, i wybierając uprzednio utworzony pędzel oraz czcionkę w
kontekście urządzenia:
CFont* pOldFont = (CFont*)pDC->SelectObject(&m_fnTimes);
CBrush* pOldBrush = (CBrush*)pDC->SelectObject(&m_brHatch);
Uruchomiwszy aplikację po wprowadzeniu tych zmian nie dostrzeżemy zapewne żadnej
różnicy. Program robi dokładnie to samo, proces drukowania i wyświetlania podglądu
wydruku będzie jednak przebiegać szybciej. W przypadku dużego, na przykład 100-
stronicowego dokumentu używającego różnych zasobów GDI, ten sposób definiowania
zasobów jest znacznie praktyczniejszy.
Korzystanie ze współrzędnych z funkcji OnBeginPrinting ( )
Mogłoby nas kusić, by przechować w funkcji OnBeginPrintingO również współrzędne prostokąta
wydruku. To jednak nie przyniesie efektu, gdyż zmienna składowa in_rectDraw klasy CPrintinfo nie
została jeszcze inicjowana i program, zamiast wybranych, wpisze po prostu losowe wymiary.
fAiru. i/v\z.c
O tworzeniu obiektu pędzla G D l pisaliśmy w rozdziale 16.
O tworzeniu obiektu czcionki G D l pisaliśmy w rozdziale 18.
Jak załączać zmienną składową za pomocą Classview pisaliśmy w rozdziale 19.
Modyfikowanie kontekstu urządzenia
Jeszcze przed wezwaniem funkcji OnDraw () i Onprint (), przyzywana jest wirtualna funkcja
przygotowująca kontekst urządzenia OnPrepareDC (). Funkcja ta może zostać pokryta w klasie widoku,
aby wprowadzić modyfikacje w kontekście urządzenia, które będą wspólne dla funkcji OnDraw () i
Onprint (). Można tu na przykład zdefiniować określony typ odwzorowywania lub tryb malowania
wspólny dla procesu drukowania i wyświetlania na ekranie. Funkcja pokrywająca nie jest tworzona przez
kreator AppWi-zard, ale można ją bez problemu dodać do programu za pomocą okna dialogowego Add
Virtual Function. W tworzonym tutaj przykładzie wspólnym elementem funkcji OnDraw () i OnPrintO
jest funkcja SetTextAlign ( ) kontekstu urządzenia. Możemy dodać ją do funkcji OnPrepareDC () w
następujący sposób:
void CPrintItView::OnPrepareDC(CDC* pDC, CPrintInfo* pinfo) {
pDC->SetTextAlign(TA_CENTER+TA_BASELINE) ;
}
Czasami, szczególnie wówczas, gdy programujemy procedurę drukowania, w której
wydruk jest w stu procentach zgodny z tym co użytkownik widzi na ekranie (w oryginale
angielskim stosowany jest akronim WYSIWYG, patrz słownik*), wygodnie jest definiować
tryby odwzorowywania i rozmiary okna we wspólnej funkcji przyzywanej przed odwołaniem
do odpowiedniej funkcji drukującej lub rysującej na ekranie. Właściwym miejscem do
konfiguracji kontekstu urządzenia jest funkcja OnPrepareDC ().
PATRZ TAKŻE
Na temat kontekstu urządzenia pisaliśmy w rozdziale 15.
Justowanie czcionki opisane zostało w rozdziale 17.
Przerywanie wydruku
Funkcję OnPrepareDC () można również wykorzystać do przerywania wydruku i innych
związanych z dokumentem funkcji. Jeśli drukowany jest szczególnie długi dokument, możemy
udostępnić użytkownikowi opcję pozwalającą na przerwanie drukowania. Funkcja AbortDoc ()
umożliwia przerwanie wydruku dokumentu. Jeśli do funkcji OnPre-
przyp. tłum.
Drukowanie i podgląd wydruku______ 589
pareDC () dodamy następujące linie, przerwiemy proces drukowania po wydrukowaniu trzech stron:
if (pDC->IsPrinting())
if (pInfo->m_nCurPage==3) pDC->AbortDoc();
Drukowanie bez pomocy funkcji szkieletu aplikacji
Jak dotąd opisywaliśmy obsługę drukowania w oparciu o funkcje dostarczane automatycznie przez
szkielety aplikacji SDI i MDI. Dostarczana przez nie obsługa drukowania działa bardzo sprawnie, ale
zdarza się, że potrzebny nam jest szybki i prosty dostęp do drukarki lub sytuacje gdy nie możemy
korzystać z pomocy szkieletu aplikacji, tak jak w aplikacjach opartych na oknach dialogowych.
Szkielet aplikacji ukrywa przed naszymi oczami odpowiedzialne za wykonywanie zadań
związanych z drukowaniem funkcje niskiego poziomu. Tutaj opiszemy pokrótce jak funkcje te działają i
pokażemy drukowanie na przykładzie aplikacji opartej na oknach dialogowych.
Bezpośrednie przyzywanie okna dialogowego Print
W podrozdziale poświęconym korzystaniu z okna dialogowego Print, pokazaliśmy, w jaki sposób
klasa cprintDialog obudowuje okno dialogowe PRINTDLG i w jaki sposób przywoływać to okno w funkcji
CView: :DoPreparePrinting ().
Okno to można również wykorzystać bezpośrednio do zdefiniowania drukarki docelowej i
odpowiednich ustawień, w taki sam sposób, w jaki przyzywamy normalne modalne (ang. modal) okno
dialogowe. Za pomocą tej samej funkcji dostępu można również zdefiniować drukowane strony i
domyślną liczbę kopii dokumentu, dokładnie tak jak w funkcji
DoPreparePrintingO szkieletu aplikacji.
Listing 22.7 pokazuje, w jaki sposób można to okno dialogowe wykorzystać do skonfigurowania
drukarki do potrzeb aplikacji bazującej na oknach dialogowych, a następnie w oparciu o ustawienia
zdefiniowane w oknie dialogowym do wydrukowania niewielkich rozmiarów dokumentu.
Tryb CreateDC
Zamiast wyświetlać okno dialogowe Print można za pomocą wbudowanych w nie funkcji utworzyć
odpowiednio skonfigurowany kontekst urządzenia. Tak utworzony kontekst urządzenia można
następnie spokojnie wykorzystać do drukowania. W tym celu należy skorzystać z funkcji składowej
CreatePrinterDC () klasy cprintDia-log. Domyślne ustawienia kontekstu urządzenia można również
zmieniać, zmieniając wartości wchodzące w skład struktur DEVMODE i DEVNAMES. Wskaźniki do tych
struktur można zdobyć za pomocą funkcji GetDevMode (), a strukturę DEVNAMES można znaleźć za
pomocą zmiennej składowej m_pd struktury PRIBNTDLG.
590___________________________________Poznaj Visual C++ 6
Pokazany na tym listingu mechanizm drukowania opiera się na funkcjach Start-Doc () i EndDoc (),
które opiszemy za chwilę.
Za pomocą kreatora AppWizard tworzymy aplikację DIgPrint opartą na oknach dialogowych, a za
pomocą kreatora CIassWizard funkcję obsługi, do której wpiszemy kod przedstawiony na listingu 22.7.
Listing 22.7. LST23_7.CPP - obsługa bezpośredniego drukowania w funkcji OnOK() aplikacji bazującej
na oknie dialogowym
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void CDIgPrintDlg::OnOK() (
// TODO: Tutaj dodaj własny kod kontrolujący
II** Skonstruuj obiekt CPrintDialog CPrintDialog
dIgPrint(FALSE,PD_ALLPAGES,this) ;
if (dlgPrint.DoModal()==IDOK) {
// ** Przypisz kontekst urządzenia okna dialogowego
II ** obiektowi COC
CDC dcPrint;
dcPrint.AttachfdIgPrint.GetPrinterDC()) ;
// ** Utwórz i zapełnij strukturę DOCINFO DOCIMFO
myPrintJob;
myPrintJob.cbSize = sizeof(myPrintJob); O
myPrintJob.IpszDocName = "MyPrintJob"; O
myPrintJob.IpszOutput = NULL; O
myPrintJob.IpszDatatype = NULL; O
myPrintJob.fwType = NULL; O
// ** Rozpocznij drukowanie dokumentu if
(dcPrint.StartDoc(SmyPrintJob)>=0) {
// ** Rozpocznij drukowanie strony
dcPrint.StartPage() ;
// ** Rozpocznij drukowanie strony
dcPrint.TextOut(0,0,"My Smali Print Job");
// ** Zakończ stronę
dcPrint.EndPage ();
Drukowanie i podgląd wydmku 591
35 // ** Zamknij dokument
36 dcPrint.EndDoc();
37 }
38
39 // ** Usuń kontekst urządzenia drukarki
40 dcPrint.DeleteDCO;
41 }
42
43 II** Wezwij bazową funkcję OnOK
44 CDialog::OnOK() ;
45 }
O Zapewniamy strukturę DOCINFO podstawowymi informacjami niezbędnymi do realizacji zadania
wydmku
Linia tekstu, którą drukujemy między wezwaniami do funkcji Startpage () i End-Page()
Tutaj musimy usunąć kontekst urządzenia utworzony przez okno dialogowe Print
W linii 6 listingu 22.7 deklarujemy obiekt digPrint należący do klasy CPrintDia-log. Konstruktor
tego okna dialogowego ma trzy parametry. Pierwszy parametr to znacznik przyjmujący wartość TRUE,
jeśli wyświetlamy okno dialogowe PrintSetup, a FALSE, jeśli wyświetlamy okno dialogowe Print. Drugi
parametr jest zbiorem dających się zestawiać w różnych kombinacjach znaczników (zbyt licznych, by je
tutaj opisywać), które pozwalają przystosować do naszych potrzeb ustawienia okna. Trzeci parametr jest
wskaźnikiem do okna rodzica; wykorzystane tutaj stówo kluczowe języka C++ this wskazuje, że
rodzicem jest okno dialogowe.
To okno dialogowe wyświetlane jest w linii 8 za pomocą funkcji digPrint. DoModal (). Jeśli
użytkownik kuknie OK, rozpoczynamy drukowanie, jeśli nie, pozostała część bloku funkcji jest
pomijana.
Jeśli użytkownik wciśnie w oknie dialogowym Print przycisk OK, tworzony jest kontekst
urządzenia dla drukarki, który w celu łatwiejszego operowania na nim przyłączany jest w linii 13 do klasy
kontekstu urządzenia (CDC). Należy pamiętać, aby na koniec (w linii 40) utworzony kontekst urządzenia
usunąć.
Aby po zbudowaniu i uruchomieniu aplikacji, wydrukować zaprogramowany w przedstawionej
przed chwilą funkcji tekst, wystarczy kliknąć w oknie dialogowym OK.
Funkcje StartDoc () i EndDoc ()
Klasa CDC kontekstu urządzenia posiada wiele związanych z drukarką funkcji. Aby rozpocząć nowy
wydruk, system Windows musi utworzyć specjalny dokument bufora
592_____________________________________Poznaj Visual C++ 6
(ang. spool document), który będzie przechowywał zadanie wydruku (ang. print job) i przesyłał je do
drukarki, gdy zostanie zakończone. Funkcja StartDoc () informuje system Windows, że powinien
rozpocząć przygotowywanie dokumentu bufora, a funkcja EndDoc (), że dokument jest już gotowy i
może zostać wysłany do drukarki. Przedstawiona wcześniej funkcja AbortDoc () przerywa zadanie
wydruku, tak że dokument nie zostaje wysłany do drukarki.
Na listingu 22.7 przyzywamy funkcję składową kontekstu urządzenia dcPrint, StartDoc () w linii 24,
przesyłając jej jako parametr wskaźnik do struktury DOCINFO. Struktura ta przechowuje szczegółowe
parametry zadania wydruku. Jedyny parametr, który musimy zdefiniować, to nazwa dokumentu bufora
(linia 18). Warto zwrócić uwagę, że struktura DOCINFO posiada nietypową zmienna składową cbsize
informującą o rozmiarach struktury. Rozmiar ten jest w linii 17 przypisywany zmiennej sizeof
(myprintJob). Tego rodzaju dziwne zachowania można czasem spotkać na poziomie funkcji API Win32;
dzieje się tak dlatego, że struktura DOCINFO wywodzi się ze starszych wersji języka C. Zmienna cbsize
jest niezbędna, ponieważ istnieje kilka różnych wersji struktury DOCINFO i jedynym sposobem na ich
rozróżnienie jest podanie rozmiaru potrzebnej nam struktury.
Funkcja StartDoc () po wezwaniu spróbuje uruchomić zadanie wydruku, a jeśli jej się powiedzie
poinformuje o sukcesie. Funkcja może zawieść z wielu powodów, wliczając w to zbyt mało miejsca na
dysku lub w pamięci, czy też uszkodzony sterownik drukarki. Dlatego zanim przystąpimy do
drukowania, warto sprawdzić zwracane przez nią informacje.
Obserwowanie bufora drukarki
Możemy obserwować tworzenie, dokumentu bufora wydruku, umieszczając odpowiedni
punkt kontrolny (ang. breakpoint) w funkcji OnPrintO lub po funkcji StartDoc () i
otwierając ikonę statusu drukarki w grupie Printers w opcji żettings głównego menu Start
systemu Windows.
Po utworzeniu dokumentu bufora powinniśmy (tak jak to zostało pokazane w linii 36)
wezwać funkcję EndDoc (), która rozpocznie drukowanie dokumentu.
PATRZ TAKŻE
^ Więcej na temat funkcji API i funkcji API Win 32 znaleźć można w rozdziale 28.
Funkcje StartPage () i EndPage ()
Kolejną ciekawą związaną z kontekstem urządzenia drukarki parą funkcji są funkcje StartPage () i
EndPage (). Funkcja StartPage () służy do inicjowania kontekstu urządzenia, tak aby przygotować go do
drukowania kolejnej strony. W ten sposób przywrócone zostaną pewne początkowe ustawienia kontekstu
urządzenia, takie jak początkowa
Drukowanie i podgląd wydruku 593
pozycja kursora rysującego elementy graficzne, a w dokumencie bufora ustawione zostaną odpowiednie
informacje definiujące początek strony.
Zazwyczaj funkcję StartPageO przyzywamy, by w kontekście urządzenia narysować pewne
szczegóły graficzne, które powinny być narysowane na danej stronie. Następnie przyzywamy funkcję
OnEndPage (), która zapisuje stronę w pliku bufora i dodaje ją do dokumentu bufora.
Na listingu 22.7 funkcja StartPage () jest przyzywana w linii 27. Po niej przywołujemy pojedynczą
funkcję Text0ut () wpisującą na drukowanej stronie pożądany tekst, by na koniec odwołać się w linii 33
do funkcji EndPage ().
Łączenie się bezpośrednio z urządzeniem
Teoretycznie pisząc program nie powinniśmy zaprzątać sobie głowy tym, jaka drukarka jest podłączona
do komputera, na którym działać będzie aplikacja. W razie potrzeby możemy jednak obejść
dostarczany przez Windows mechanizm kontekstu urządzenia i przesłać ciąg znaków bezpośrednio do
drukarki.
Pozwala, na to dostępna w klasie coc funkcja Escape (), która umożliwia przesłanie fragmentu kodu
bezpośrednio do podłączonego urządzenia. Funkcje Escape () odnoszące się do różnych urządzeń, są
rozróżniane w oparciu o kod, który zawierają i mogą odnosić się albo do konkretnego urządzenia
(przesyłane są wtedy bezpośrednio do urządzenia), albo do sterownika dostarczanego przez system
Windows (są na potrzeby urządzenia tłumaczone przez sterownik).
Do funkcji tych sięgamy zazwyczaj, gdy któraś z funkcji urządzenia nie jest obsługiwana przez
standardowy mechanizm systemu Windows.
Przyzwanie funkcji EndPage () powoduje przesłanie specjalnego kodu drukarki informującego o
tym, że strona jest kompletna (Form Feed) i dokument bufora rejestruje kolejną stronę. Aby wydrukować
wielostronicowy dokument, trzeba obie funkcje przyzwać odpowiednią liczbę razy, a na zakończenie
przyzwać uruchamiającą wydruk funkcję EndDocO. Pomiędzy funkcjami Start Page() i EndPage ()
można zapełniać poszczególne strony korzystając z funkcji OnPrint () dokładnie w taki sam sposób, w
jaki robiliśmy to wcześniej w aplikacji SDI. Funkcje te są również przyzywane w aplikacji SDI, ale
szkielet aplikacji ukrywa je przed nami, wzywając tylko w odpowiednich momentach funkcję OnPrint ().
Wyszukiwarka
Podobne podstrony:
USTAWA O OCHRONIE OSÓB I MIENIA Z 22 SIERPNIA 1997 R
przykladowyJrkusz150UM[1] drukow
E 22 Of Domine in auxilium
wydruk
BAZA PYTAŃ 22
ustawa 22 poz 251 z 2001r
22 Ostrzeganie i alarmowanie
HP Zarządzanie i drukowanie
22 Planowanie podstawowego żywienia dietetycznego
więcej podobnych podstron