Sign in Join
Wiki copytemporary

Using the printer/pl: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Using the printer/pl

OldNewLine
1+Korzystanie z drukarki
2+
3+## Wstęp
4+
5+Drukowanie jest łatwe w Lazarus/FreePascal. Musisz jednak wykonać kilka wymaganych kroków. Najpierw pierwsze kroki. Po tych pierwszych krokach możesz przejść do bardziej zaawansowanego drukowania. W tym artykule omówiono wymagane kroki zebrane z różnych stron forum i przykładów. Po wykonaniu podstawowych kroków wydrukujemy trochę tekstu. Na koniec podana jest wskazówka dotycząca bardziej zaawansowanego drukowania.
6+
7+> **Note:** Obecnie drukowanie w WinCE (przy użyciu pakietu Printers4Lazarus) nie jest obsługiwane.
8+
9+## Podstawowe kroki
10+
11+Aby móc korzystać z drukarek, musisz wykonać następujące czynności:
12+
13+1. Dodaj pakiet Printer4Lazarus do wymagań projektu.
14+2. Dodaj moduł Printers do sekcji uses w swoim module.
15+3. Użyj istniejącego obiektu Printer.
16+
17+### Dodanie pakietu Printer4Lazarus do wymagań projektu
18+
19+Pakiet Printer4Lazarus definiuje podstawową drukarkę i zapewnia drukowanie niezależne od platformy. Dzięki temu na różnych platformach można korzystać z poniższych rzeczy.
20+
21+W Lazarus IDE wykonaj następujące czynności:
22+
23+1. W menu **Projekt** kliknij **Inspektor projektu**. Wyświetlane jest okno z widokiem drzewa, a jedna z gałęzi nazywa się *Wymagane pakiety*. Domyślnie gałąź *Wymagane pakiety* pokazuje pakiet *LCL*.
24+2. Kliknij przycisk **Dodaj**, czyli przycisk ze znakiem plus u góry okna.
25+3. Otwórz stronę **Nowy wymóg**.
26+4. Z listy **Nazwa pakietu** wybierz *Printer4Lazarus*.
27+5. Kliknij OK'''.
28+6. *Printer4Lazarus* jest teraz pokazywany w gałęzi *Wymagane pakiety*.
29+
30+### Dodanie modułu Printers do sekcji uses twojego modułu
31+
32+Ten krok jest prosty, a wynik może wyglądać tak:
33+
34+``` pascal
35+unit MainUnit;
36+
37+{$mode objfpc}{$H+}
38+
39+interface
40+
41+uses
42+ Classes, SysUtils, Forms, Printers;
43+```
44+
45+### Korzystanie z istniejącego obiektu Printer
46+
47+Załóżmy, że chcesz kliknąć przycisk, aby wydrukować tekst. W formularzu umieść przycisk o nazwie **PrintBtn**, a w zdarzeniu **OnClick** możesz teraz użyć:
48+
49+``` pascal
50+procedure TForm1.PrintBtnClick(Sender: TObject);
51+const
52+ LEFTMARGIN = 100;
53+ HEADLINE = 'Data i czas mojego pierwszego wydruku to: ';
54+var
55+ YPos, LineHeight, VerticalMargin: Integer;
56+ SuccessString: String;
57+begin
58+ with Printer do
59+ try
60+ BeginDoc;
61+ Canvas.Font.Name := 'Courier New';
62+ Canvas.Font.Size := 10;
63+ Canvas.Font.Color := clBlack;
64+ LineHeight := Round(1.2 * Abs(Canvas.TextHeight('I')));
65+ VerticalMargin := 4 * LineHeight;
66+ // No to jedziemy
67+ YPos := VerticalMargin;
68+ SuccessString := HEADLINE + DateTimeToStr(Now);
69+ Canvas.TextOut(LEFTMARGIN, YPos, SuccessString);
70+ finally
71+ EndDoc;
72+ end;
73+end;
74+```
75+
76+Czy napisałem *podstawowy* i *prosty*? Powyższy przykład jest nieco złożony. Oprócz podstawowego tekstu wyjściowego w pogrubionej linii, zawiera również przykład formatowania tekstu.
77+
78+Od słów kluczowych *begin* do *end*; dzieją się następuje rzeczy:
79+
80+- Wraz z *Printer.BeginDoc;* rozpoczynasz drukowanie - jednak nic nie jest wysyłane do drukarki, dopóki nie zakończysz czynności poleceniem *Printer.EndDoc;*.
81+- Drukarka używa płótna (canvas) do rysowania wydruku. To właśnie ten rysunek trafia na wydrukowaną stronę. *Canvas.Font* to obiekt czcionki używany na wyjściowym płótnie. Oznacza to, że wywołanie *TextOut*, którego użyjemy później, wyświetli w tym momencie tekst przy użyciu ustawień obiektu czcionki.
82+- Wszystko, co rysujesz na płótnie, musi być ustawione za pomocą współrzędnych. Tak więc obliczamy *LineHeight*, aby ustawić tekst w pionie. Możesz zrobić to samo dla pozycji poziomej, którą tutaj zostawiłem jako *LEFTMARGIN*.
83+- Tekst jest rysowany za pomocą wywołania *TextOut*.
84+- Ten wspaniały wynik jest wysyłany do drukarki przez *Printer.EndDoc*.
85+
86+> **Note:** Na niektórych forach sugeruje się, że do prawidłowego funkcjonowania wymagane jest użycie *PrintDialog* (okno dialogowe wyboru drukarki), ale nie uznałem tego prawdę (nigdy więcej).\
87+> \
88+>
89+> > **Tip:** Niestety jest to prawda, choć nie pełna. Przy pierwszym uruchomieniu powyższego przykładu może się zdarzyć, że pojawi się błąd Access Violation. Nie wiem jeszcze jaka jest przyczyna tego błędu. Gdy położymy *PrintDialog* na formatce, to błąd już się nie pojawia. Co więcej, można ten *PrintDialog* usunąć, a błąd już nie wraca. (Ten problem zaobserwowałem w systemie Fedora. Niestety teraz nie mogę go odtworzyć. Prawdopodobnie problem dotyczy plików konfiguracyjnych w katalogu .lazarus)
90+
91+## Następne kroki
92+
93+Po wykonaniu powyższych podstawowych kroków, możesz wykonać kolejne kroki. Zostawiam to tobie czytelniku, abyś spróbował:
94+
95+- Zrób rysunki na papierze.
96+- Ładnie sformatuj tekst.
97+- Wybierz inną drukarkę i porównaj wyniki.
98+
99+W dystrybucji Lazarusa znajduje się przykładowy projekt, który wykorzystuje do drukarki drukowanie w trybie Raw. Ten przykład można znaleźć w następującej lokalizacji \$(lazarusdir)\components\printers\samples\rawmode\rawmodetest.lpi. Kolejny przykład pokazuje, jak wybrać inną drukarkę: \$(lazarusdir)\components\printers\samples\dialogs\selectprinter.lpi.
100+
101+## Zaawansowane kroki: sterowanie drukowaniem
102+
103+Obiekt drukarki umożliwia przede wszystkim rysowanie na płótnie i wysyłanie tego obrazu do drukarki. Jeśli będziemy kontynuować ścieżką pokazaną powyżej, użyjesz metod płótna drukarki, aby narysować tekst, elipsy, romby i co tylko chcesz.
104+
105+Nie może to jednak zainteresować żadnego poważnego programisty. Pracujesz nad doskonalszym programem CAD lub jeszcze innym sortownikiem plików graficznych i chcesz wydrukować wspaniały rezultat swojego programu na swojej drukarce. Próba przetłumaczenia idealnego obrazu na wywołania metod na płótnie nie jest dobrą drogą: ponieważ ty już masz gotowy obraz.
106+
107+Każda kontrolka, którą umieścisz w formularzu, również rysuje obraz na obiekcie TCanvas, podobnie jak drukarka. Możemy to wykorzystać do przeniesienia obrazu z ekranu do drukarki.
108+
109+Wyobraź sobie, że zamierzasz stworzyć program podglądu wydruku. Tworzysz formularz i na tym formularzu umieszczasz TPanel. Ten panel zapewni ładne szarawe tło podglądu. Na panelu umieszczasz kolejny obiekt TPanel o nazwie *page* (strona). Ten panel *page* będzie biały i reprezentuje papier. Możesz ładnie dopasować rozmiar strony.
110+
111+Na tej stronie umieściliśmy obiekt TShape, na przykład ładny czerwony, zaokrąglony prostokąt. Teraz spróbuj wykonać następujące czynności w metodzie zdarzenia **PrintBtnClick**:
112+
113+``` pascal
114+MyPrinter.BeginDoc;
115+ page.PaintTo(myPrinter.Canvas, 0, 0);
116+MyPrinter.EndDoc;
117+```
118+
119+Co tu się dzieje?
120+
121+- *BeginDoc* rozpoczyna drukowanie (ale nic nie zostało jeszcze wysłane).
122+- *page.PaintTo* wysyła wyjście naszego obiektu TPanel reprezentującego stronę na płótno drukarki. Zwróć uwagę na następujące kwestie:
123+ 1. Możesz użyć metody PaintTo dowolnej kontrolki w hierarchii kontrolek. Możesz również wysłać dane wyjściowe całego okna do drukarki, jeśli chcesz.
124+ 2. Możesz wysłać wynik dowolnej kontrolki za pomocą metody PaintTo do drukarki, dzięki czemu możesz być kreatywny. Aby wysłać dane wyjściowe z sortera obrazów do drukarki, można wysłać dane wyjściowe TImage do drukarki.
125+ 3. TCanvas również posiada metodę kopiowania prostokątów z innego płótna. Jednak możesz to zrobić tylko wtedy, gdy obiekt naprawdę narysowany jest na płótnie. Myślę, że większość kontrolek polega na kontenerach, aby zapewnić prawdziwe płótno, więc nie można kopiować prostokątów z dowolnej kontrolki. Przynajmniej dla mnie to nie zadziałało.
126+ 4. Upewnij się, że kontrolka, którą chcesz pomalować na drukarce, jest widoczna. Jeśli kontrolka nie jest widoczna, nic nie zostanie namalowane nawet na drukarce.
127+- *EndDoc* wysyła rysunek do drukarki.
128+
129+## Zmiana rozmiaru papieru
130+
131+Drukarka zużywa znacznie więcej pikseli na cal na papierze niż monitor używa pikseli na cal na ekranie. W rezultacie wydruk końcowy, który jest przekierowywany z ekranu do drukarki, ma na papierze rozmiar raczej niewielki. Skalowanie i kontrolowanie układu jest ważne dla dobrego wyglądu wydruku. Byłoby miło, gdybyś mógł mieć kopię tego, co widzisz na ekranie w dokładnym rozmiarze.
132+
133+Na razie nie dążymy do ideału, tylko do idei. Jak wspomniano wcześniej, kontrolki nie mają własnego płótna, ale opierają się na płótnie kontenera lub właściciela. Istnieją jednak komponenty, które mają własne płótno. Gdy wybrałem TBitMap, działa w następujący sposób:
134+
135+``` pascal
136+procedure TForm1.PrintBtnClick(Sender: TObject);
137+var
138+ MyPrinter : TPrinter;
139+ myBitMap : TBitMap;
140+begin
141+ myBitMap := TBitMap.Create;
142+ myBitMap.Width := page.Width;
143+ myBitMap.Height := page.Height;
144+ page.BorderStyle:=bsNone;
145+ page.PaintTo(myBitMap.Canvas, 0, 0);
146+ page.BorderStyle:=bsSingle;
147+ //
148+ MyPrinter := Printer;
149+ MyPrinter.BeginDoc;
150+ //page.PaintTo(myPrinter.Canvas, 0, 0);
151+ //myPrinter.Canvas.Draw(0,0, myBitMap);
152+ myPrinter.Canvas.CopyRect(Classes.Rect(0, 0, myPrinter.PaperSize.Width, myPrinter.PaperSize.Height),
153+ myBitMap.Canvas, Classes.Rect(0, 0, myBitMap.Width, myBitMap.Height));
154+ MyPrinter.EndDoc;
155+ myBitMap.Free;
156+end;
157+```
158+
159+Aby to zadziałało, nie używaj modułu *Windows*. Moduł *Windows* ma inne definicje dla **Rect**. To, co widzisz w przykładzie, działa w następujący spsób:
160+
161+- Tworzona jest mapa bitowa o takim samym rozmiarze jak kontrolka *page*.
162+- Aby uniknąć drukowania ramki, to **BorderStyle** kontrolki *page* jest wyłączany przed namalowaniem jej na bitmapie, a następnie przywracany do poprzedniej wartości.
163+- Następnie rozpoczyna się drukowanie, a zawartość płótna *BitMap* jest kopiowana na płótno drukarki.
164+
165+Ale zauważ jedną rzecz, że w tym procesie strona (*page*) jest powiększana. **Printer.papersize** jest znacznie większy niż rozmiar mapy bitowej (bitmap), ale proces kopiowania ładnie wypełnia docelowy prostokąt. Tak więc, kiedy chcemy to zrobić, musimy upewnić się, że wymiary *page* mają taki sam stosunek szerokości do wysokości, jak wymiar papieru. Możesz dowiedzieć się, jak to zrobić.
166+
167+Problem z tym sposobem pracy polega oczywiście na tym, że na wydrukowanym papierze pojawią się piksele ekranu. Jak powiedziałem, nie jest to sposób idealny, ale pokazuje zasadę działania. Kontrolki nie mają własnego płótna; aby wydrukować kontrolki, najpierw malujemy je na obiekcie, który ma swoje własne płótno: TBitMap. Teraz wiesz, jak to działa, możesz wymyślić sposób na tworzenie pięknych dzieł sztuki lub dokumentów. Będzie to wymagało obiektów, które rysują się poprawnie w TPanel o niskiej rozdzielczości, ale także na TBitMap o wysokiej rozdzielczości. Ale to jest temat na inny artykuł.
168+
169+## Zwykłe zadania
170+
171+Pamiętaj, że **Printer: TPrinter** to singleton (może istnieć tylko jedna instancja obiektu tej klasy). Jest to pojedynczy obiekt, który obsługuje wszystkie drukarki zainstalowane w systemie. Konkretna realizacja w systemie (WinAPI, Cocoa itp.) powinna implementować ten obiekt.
172+
173+### Wyliczanie dostępnych drukarek
174+
175+Lista drukarek systemowych jest dostępna we właściwości **Printer.Printers**.
176+
177+Właściwość **Printers** jest typu TStrings, która zawiera nazwę każdej drukarki.
178+
179+```delphi
180+procedure TForm1.FormShow(Sender: TObject);
181+begin
182+ ListBox1.Items.AddStrings(Printer.Printers);
183+end;
184+```
185+
186+Wybranej nazwy podanej we właściwości **Printers** należy użyć ustawiając drukarkę za pomocą metody SetPrinter().
187+
188+**Internals:** każda klasa implementująca klasę **TPrinter** powinna implementować metodę **DoEnumPrinters()**. DoEnumPrinters powinien wypełnić przekazane parametry listy nazwami drukarek. Oczekuje się, że nazwa drukarki jest unikalna.
189+
190+**DoEnumPrinters()** powinien zwrócić domyślną drukarkę systemową jako pierwszy wpis (indeks 0) na liście.
191+
192+### Zmiana/Wybór drukarki
193+
194+Domyślnie obiekt Printer powinien mieć wstępnie wybraną drukarkę „domyślną systemową”. (Z tym, że w systemie Windows 10 i nowszych może nie być ustawiona drukarka domyślna.)
195+
196+W razie potrzeby drukarka może zostać zmieniona przez użytkownika za pośrednictwem PrinterDialogs lub programowo.
197+
198+W celu programowej zmiany drukarki należy użyć metody **SetPrinter** lub właściwości **PrinterIndex**.
199+
200+Obie metody opierają się na drukarkach wymienionych w **PrintersProperty**.
201+
202+- **PrinterIndex** określa nazwę drukarki na podstawie określonego indeksu z **PrintersList**, a następnie wywołuje metodę **SetPrinter**.
203+
204+```delphi
205+procedure TForm1.Button2Click(Sender: TObject);
206+begin
207+ Printer.SetPrinter(ListBox1.ItemIndex);
208+end;
209+```
210+
211+:*Jeśli określony indeks wynosi -1, to znaczy, że wybrana została drukarka domyślna
212+
213+- Metoda **SetPrinter**, sprawdza, czy żądana nazwa istnieje na liście właściwości **Printers**.
214+
215+```delphi
216+procedure TForm1.Button2Click(Sender: TObject);
217+begin
218+ Printer.SetPrinter(ListBox1.GetSelectedText);
219+end;
220+```
221+
222+:* Zauważ, że istnieje specjalna nazwa dla metody **SetPrinter**. Jeśli nazwa jest określona jako '*', wybierana jest pierwsza (domyślna) dostępna drukarka. Każda z metod może zgłosić wyjątek, jeśli określona nazwa lub indeks nie istnieje.
223+
224+**Internals:** Klasa pochodna od **TPrinter** powinna implementować metodę **DoSetPrinter()** używając parametru **aName** jako nazwy wybranej drukarki. Metoda nie wymaga specjalnej obsługi nazwy '*', ponieważ jest ostrożna na wyższym poziomie niż TPrinter.
225+
226+### Przygotowywanie rozmiaru papieru
227+
228+Uwaga: jeśli używasz wielu drukarek (zwłaszcza jeśli używasz specjalistycznych drukarek używających niestandardowego rozmiaru papieru, takiego jak „A4” (europejski) lub „US Letter” (USA)), zawsze chcesz przygotować właściwy rozmiar papieru. Możliwe, że nowo wybrana drukarka nie obsługuje papieru, który był obsługiwany przez poprzednio wybraną drukarkę. Rozmiar papieru nie jest resetowany automatycznie, więc musisz to zrobić samodzielnie.
229+
230+Istnieją trzy właściwości **PaperSize**, które można wywołać w celu zresetowania i przygotowania rozmiaru papieru:
231+
232+- **PaperName** - zwraca aktualnie wybraną nazwę papieru wybranej drukarki
233+- **DefaultPaperName** - zwraca domyślną nazwę papieru dla wybranej drukarki
234+- **SupportedPapers** - zwraca listę rozmiarów papieru obsługiwanych przez wybraną drukarkę
235+
236+Oto przykład kodu, który ponownie odczytuje listę wybranych rozmiarów papierów
237+
238+```delphi
239+procedure TForm1.ListBox1SelectionChange(Sender: TObject; User: boolean);
240+begin
241+ // zmieniono drukarkę. Ale wybrany rozmiar papieru nadal może być właściwy
242+ Printer.PrinterIndex := ListBox1.ItemIndex;
243+ ListBox2.Clear;
244+ // wywołanie SupportedPapers wymuszające weryfikację rozmiaru papieru
245+ ListBox2.Items.AddStrings(Printer.PaperSize.SupportedPapers);
246+end;
247+```
248+
249+Oto przykład resetowania rozmiaru papieru przy użyciu właściwości **DefaultPaperName**.
250+
251+```delphi
252+procedure TForm1.ListBox1SelectionChange(Sender: TObject; User: boolean);
253+begin
254+ // zmieniono drukarkę. Ale wybrany rozmiar papieru nadal może być właściwy
255+ Printer.PrinterIndex := ListBox1.ItemIndex;
256+ // wywołanie DefaultPaperName wymuszające weryfikację rozmiaru papieru
257+ Printer.PaperSize.DefaultPaperName;
258+end;
259+```
260+
261+**Internals:** Pochodna klasa **TPrinter** powinna poprawnie implementować:
262+
263+- **Metodę DoEnumPapers()**. Metoda ta może użyć właściwości **PrinterIndex** lub **PrinterName** do określenia aktualnie wybranej drukarki, do której należy wyliczyć rozmiary papieru.
264+
265+Jeśli metoda wewnętrzna nie zwraca żadnych rozmiarów papieru, klasa **TPrinter** zastępuje je „domyślnymi” rozmiarami papieru (takimi, które przypuszczalnie obsługiwane są przez większość drukarek). Domyślne rozmiary to np.: **Letter**, **A4**, **Legal**. Jednak dobra implementacja klasy TPrinter powinna wypełniać rozmiary stron.
266+
267+- Metoda **DoGetPaperRect()**. Metoda zwraca fizyczny rozmiar papieru określony przez nazwę bieżącej drukarki. Nazwa papieru jest przekazywana przez właściwość **aName**. Zwracana wartość powinna być wypełniona do struktury **TPaperRect**.
268+
269+Należy wypełnić także dwa pola:
270+
271+- PhysicalRect - rzeczywisty rozmiar całej strony. Rozmiar podany jest w punktach. Oczekuje się, że Left i Top będą zerami.
272+- WorkRect - prostokąt, w którym drukarka może faktycznie drukować (ze względu na pewne mechaniczne ograniczenia samej drukarki). Prostokąt powinien być zawsze taki sam lub mniejszy niż PhysicalRect. Left i Top są odsunięciami od lewego górnego rogu fizycznego prostokąta. Jednostkami są punkty drukarki.
273+
274+Liczba punktów zależy od aktualnie wybranego DPI drukarki (ang. dots per inch - liczba punktów obrazu na cal długości). (Zauważ, że istnieją dwa różne DPI dla pomiarów poziomych i pionowych).
275+
276+Oto tabela przedstawiająca różnicę w rozmiarze A4 w zależności od rozmiaru DPI
277+
278+| | |
279+|------|-------------------------------|
280+| DPI | Zwrócony rozmiar w TPaperRect |
281+| 72 | 595 x 842 |
282+| 300 | 2480 x 3507 |
283+| 600 | 4960 x 7014 |
284+| 1200 | 9920 x 14028 |
285+
286+- **DoGetDefaultPaperName()** - metoda powinna zwrócić nazwę domyślnego rozmiaru papieru, dla aktualnie wybranej drukarki.
287+- **DoGetPaperName** - metoda powinna zwrócić nazwę aktualnie wybranego rozmiaru papieru, dla aktualnie wybranej drukarki.
288+- **DoSetPaperName** - metoda powinna ustawić rozmiar papieru według nazwy dla wybranej drukarki.
289+
290+## Zobacz także
291+
292+- [[Printing/pl|Drukowanie]]
293+- [[LazReport Documentation|Dokumentacja do LazReport]] LazReport może być używany do projektowania raportów opartych na danych, które można drukować, eksportować do formatu PDF itp.
294+- [[LazReport Tutorial|Poradnik do LazReport]]
295+
296+---
297+*From the Free Pascal wiki, page "Using the printer/pl", as it stood on 2022-02-06 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history