]>
Commit | Line | Data |
---|---|---|
2bda0e17 | 1 | ///////////////////////////////////////////////////////////////////////////// |
7520f3da | 2 | // Name: src/msw/printwin.cpp |
2bda0e17 KB |
3 | // Purpose: wxWindowsPrinter framework |
4 | // Author: Julian Smart | |
5 | // Modified by: | |
6 | // Created: 04/01/98 | |
7 | // RCS-ID: $Id$ | |
6c9a19aa | 8 | // Copyright: (c) Julian Smart |
65571936 | 9 | // Licence: wxWindows licence |
2bda0e17 KB |
10 | ///////////////////////////////////////////////////////////////////////////// |
11 | ||
103aec29 VZ |
12 | // =========================================================================== |
13 | // declarations | |
14 | // =========================================================================== | |
15 | ||
16 | // --------------------------------------------------------------------------- | |
17 | // headers | |
18 | // --------------------------------------------------------------------------- | |
19 | ||
2bda0e17 KB |
20 | // For compilers that support precompilation, includes "wx.h". |
21 | #include "wx/wxprec.h" | |
22 | ||
23 | #ifdef __BORLANDC__ | |
103aec29 | 24 | #pragma hdrstop |
2bda0e17 KB |
25 | #endif |
26 | ||
12bdd77c JS |
27 | // Don't use the Windows printer if we're in wxUniv mode and using |
28 | // the PostScript architecture | |
29 | #if wxUSE_PRINTING_ARCHITECTURE && (!defined(__WXUNIVERSAL__) || !wxUSE_POSTSCRIPT_ARCHITECTURE_IN_MSW) | |
f6bcfd97 | 30 | |
2bda0e17 | 31 | #ifndef WX_PRECOMP |
57bd4c60 | 32 | #include "wx/msw/wrapcdlg.h" |
0c589ad0 BM |
33 | #include "wx/window.h" |
34 | #include "wx/msw/private.h" | |
103aec29 VZ |
35 | #include "wx/utils.h" |
36 | #include "wx/dc.h" | |
37 | #include "wx/app.h" | |
38 | #include "wx/msgdlg.h" | |
0c589ad0 | 39 | #include "wx/intl.h" |
e4db172a | 40 | #include "wx/log.h" |
6d50343d | 41 | #include "wx/dcprint.h" |
25a3fca2 VS |
42 | #include "wx/dcmemory.h" |
43 | #include "wx/image.h" | |
2bda0e17 KB |
44 | #endif |
45 | ||
25a3fca2 VS |
46 | #include "wx/msw/dib.h" |
47 | #include "wx/msw/dcmemory.h" | |
2bda0e17 | 48 | #include "wx/msw/printwin.h" |
f4322df6 | 49 | #include "wx/msw/printdlg.h" |
2bda0e17 | 50 | #include "wx/msw/private.h" |
888dde65 | 51 | #include "wx/msw/dcprint.h" |
2bda0e17 KB |
52 | |
53 | #include <stdlib.h> | |
2bda0e17 | 54 | |
2bda0e17 | 55 | #ifndef __WIN32__ |
103aec29 | 56 | #include <print.h> |
2bda0e17 KB |
57 | #endif |
58 | ||
103aec29 VZ |
59 | // --------------------------------------------------------------------------- |
60 | // private functions | |
61 | // --------------------------------------------------------------------------- | |
62 | ||
2bda0e17 KB |
63 | LONG APIENTRY _EXPORT wxAbortProc(HDC hPr, int Code); |
64 | ||
103aec29 VZ |
65 | // --------------------------------------------------------------------------- |
66 | // wxWin macros | |
67 | // --------------------------------------------------------------------------- | |
68 | ||
103aec29 VZ |
69 | IMPLEMENT_DYNAMIC_CLASS(wxWindowsPrinter, wxPrinterBase) |
70 | IMPLEMENT_CLASS(wxWindowsPrintPreview, wxPrintPreviewBase) | |
2bda0e17 | 71 | |
103aec29 VZ |
72 | // =========================================================================== |
73 | // implementation | |
74 | // =========================================================================== | |
75 | ||
76 | // --------------------------------------------------------------------------- | |
77 | // Printer | |
78 | // --------------------------------------------------------------------------- | |
7bcb11d3 | 79 | |
103aec29 VZ |
80 | wxWindowsPrinter::wxWindowsPrinter(wxPrintDialogData *data) |
81 | : wxPrinterBase(data) | |
2bda0e17 | 82 | { |
d6f2a891 | 83 | m_lpAbortProc = (WXFARPROC)wxAbortProc; |
2bda0e17 KB |
84 | } |
85 | ||
103aec29 | 86 | wxWindowsPrinter::~wxWindowsPrinter() |
2bda0e17 | 87 | { |
33ac7e6f | 88 | // avoids warning about statement with no effect (FreeProcInstance |
e41e579f | 89 | // doesn't do anything under Win32) |
d66dcb60 | 90 | #if !defined(__WIN32__) && !defined(__NT__) |
34da0970 | 91 | FreeProcInstance((FARPROC) m_lpAbortProc); |
a17e237f | 92 | #endif |
2bda0e17 KB |
93 | } |
94 | ||
95 | bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt) | |
96 | { | |
e41e579f | 97 | sm_abortIt = false; |
7bcb11d3 | 98 | sm_abortWindow = NULL; |
103aec29 | 99 | |
7bcb11d3 | 100 | if (!printout) |
f6bcfd97 BP |
101 | { |
102 | sm_lastError = wxPRINTER_ERROR; | |
e41e579f | 103 | return false; |
f6bcfd97 | 104 | } |
103aec29 | 105 | |
e41e579f | 106 | printout->SetIsPreview(false); |
1044a386 | 107 | |
d2b354f9 JS |
108 | if (m_printDialogData.GetMinPage() < 1) |
109 | m_printDialogData.SetMinPage(1); | |
110 | if (m_printDialogData.GetMaxPage() < 1) | |
111 | m_printDialogData.SetMaxPage(9999); | |
2bda0e17 | 112 | |
103aec29 | 113 | // Create a suitable device context |
f415cab9 | 114 | wxPrinterDC *dc wxDUMMY_INITIALIZE(NULL); |
7bcb11d3 JS |
115 | if (prompt) |
116 | { | |
f415cab9 | 117 | dc = wxDynamicCast(PrintDialog(parent), wxPrinterDC); |
7bcb11d3 | 118 | if (!dc) |
e41e579f | 119 | return false; |
7bcb11d3 JS |
120 | } |
121 | else | |
122 | { | |
7bcb11d3 JS |
123 | dc = new wxPrinterDC(m_printDialogData.GetPrintData()); |
124 | } | |
103aec29 | 125 | |
7bcb11d3 | 126 | // May have pressed cancel. |
888dde65 | 127 | if (!dc || !dc->IsOk()) |
7bcb11d3 JS |
128 | { |
129 | if (dc) delete dc; | |
e41e579f | 130 | return false; |
7bcb11d3 | 131 | } |
103aec29 | 132 | |
888dde65 RR |
133 | wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl(); |
134 | ||
7bcb11d3 | 135 | HDC hdc = ::GetDC(NULL); |
5cb598ae WS |
136 | int logPPIScreenX = ::GetDeviceCaps(hdc, LOGPIXELSX); |
137 | int logPPIScreenY = ::GetDeviceCaps(hdc, LOGPIXELSY); | |
7bcb11d3 | 138 | ::ReleaseDC(NULL, hdc); |
103aec29 | 139 | |
888dde65 RR |
140 | int logPPIPrinterX = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSX); |
141 | int logPPIPrinterY = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSY); | |
7bcb11d3 JS |
142 | if (logPPIPrinterX == 0 || logPPIPrinterY == 0) |
143 | { | |
144 | delete dc; | |
f6bcfd97 | 145 | sm_lastError = wxPRINTER_ERROR; |
e41e579f | 146 | return false; |
7bcb11d3 | 147 | } |
103aec29 | 148 | |
7bcb11d3 JS |
149 | printout->SetPPIScreen(logPPIScreenX, logPPIScreenY); |
150 | printout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY); | |
103aec29 VZ |
151 | |
152 | // Set printout parameters | |
7bcb11d3 | 153 | printout->SetDC(dc); |
103aec29 | 154 | |
7bcb11d3 JS |
155 | int w, h; |
156 | dc->GetSize(&w, &h); | |
157 | printout->SetPageSizePixels((int)w, (int)h); | |
f415cab9 | 158 | printout->SetPaperRectPixels(dc->GetPaperRect()); |
d6a1743b | 159 | |
7bcb11d3 JS |
160 | dc->GetSizeMM(&w, &h); |
161 | printout->SetPageSizeMM((int)w, (int)h); | |
103aec29 | 162 | |
7bcb11d3 | 163 | // Create an abort window |
0f07e3dc | 164 | wxBusyCursor busyCursor; |
103aec29 | 165 | |
1044a386 JS |
166 | printout->OnPreparePrinting(); |
167 | ||
d2b354f9 JS |
168 | // Get some parameters from the printout, if defined |
169 | int fromPage, toPage; | |
170 | int minPage, maxPage; | |
171 | printout->GetPageInfo(&minPage, &maxPage, &fromPage, &toPage); | |
172 | ||
173 | if (maxPage == 0) | |
174 | { | |
175 | sm_lastError = wxPRINTER_ERROR; | |
e41e579f | 176 | return false; |
d2b354f9 JS |
177 | } |
178 | ||
179 | // Only set min and max, because from and to have been | |
180 | // set by the user | |
181 | m_printDialogData.SetMinPage(minPage); | |
182 | m_printDialogData.SetMaxPage(maxPage); | |
183 | ||
7bcb11d3 JS |
184 | wxWindow *win = CreateAbortWindow(parent, printout); |
185 | wxYield(); | |
103aec29 | 186 | |
f172cb82 | 187 | #if defined(__WATCOMC__) || defined(__BORLANDC__) || defined(__GNUWIN32__) || !defined(__WIN32__) |
27a9bd48 | 188 | #ifdef STRICT |
888dde65 | 189 | ::SetAbortProc((HDC) impl->GetHDC(), (ABORTPROC) m_lpAbortProc); |
27a9bd48 | 190 | #else |
888dde65 | 191 | ::SetAbortProc((HDC) impl->GetHDC(), (FARPROC) m_lpAbortProc); |
27a9bd48 | 192 | #endif |
7bcb11d3 | 193 | #else |
888dde65 | 194 | ::SetAbortProc((HDC) impl->GetHDC(), (int (_stdcall *) |
7bcb11d3 | 195 | // cast it to right type only if required |
103aec29 VZ |
196 | // FIXME it's really cdecl and we're casting it to stdcall - either there is |
197 | // something I don't understand or it will crash at first usage | |
7bcb11d3 JS |
198 | #ifdef STRICT |
199 | (HDC, int) | |
d6a1743b | 200 | #else |
7bcb11d3 | 201 | () |
d6a1743b | 202 | #endif |
7bcb11d3 JS |
203 | )m_lpAbortProc); |
204 | #endif | |
103aec29 | 205 | |
7bcb11d3 | 206 | if (!win) |
2bda0e17 | 207 | { |
223d09f6 | 208 | wxLogDebug(wxT("Could not create an abort dialog.")); |
f6bcfd97 | 209 | sm_lastError = wxPRINTER_ERROR; |
103aec29 | 210 | |
7bcb11d3 | 211 | delete dc; |
e71693c3 | 212 | return false; |
2bda0e17 | 213 | } |
7bcb11d3 | 214 | sm_abortWindow = win; |
e41e579f | 215 | sm_abortWindow->Show(); |
103aec29 VZ |
216 | wxSafeYield(); |
217 | ||
7bcb11d3 | 218 | printout->OnBeginPrinting(); |
103aec29 | 219 | |
f6bcfd97 BP |
220 | sm_lastError = wxPRINTER_NO_ERROR; |
221 | ||
e41e579f DS |
222 | int minPageNum = minPage, maxPageNum = maxPage; |
223 | ||
224 | if ( !m_printDialogData.GetAllPages() ) | |
e41e579f DS |
225 | { |
226 | minPageNum = m_printDialogData.GetFromPage(); | |
227 | maxPageNum = m_printDialogData.GetToPage(); | |
228 | } | |
229 | ||
7bcb11d3 | 230 | int copyCount; |
70846f0a VZ |
231 | for ( copyCount = 1; |
232 | copyCount <= m_printDialogData.GetNoCopies(); | |
233 | copyCount++ ) | |
7bcb11d3 | 234 | { |
e41e579f | 235 | if ( !printout->OnBeginDocument(minPageNum, maxPageNum) ) |
7bcb11d3 | 236 | { |
103aec29 | 237 | wxLogError(_("Could not start printing.")); |
f6bcfd97 | 238 | sm_lastError = wxPRINTER_ERROR; |
7bcb11d3 JS |
239 | break; |
240 | } | |
241 | if (sm_abortIt) | |
f6bcfd97 BP |
242 | { |
243 | sm_lastError = wxPRINTER_CANCELLED; | |
7bcb11d3 | 244 | break; |
f6bcfd97 | 245 | } |
103aec29 | 246 | |
7bcb11d3 | 247 | int pn; |
e41e579f DS |
248 | |
249 | for ( pn = minPageNum; | |
250 | pn <= maxPageNum && printout->HasPage(pn); | |
70846f0a | 251 | pn++ ) |
7bcb11d3 | 252 | { |
70846f0a | 253 | if ( sm_abortIt ) |
7bcb11d3 | 254 | { |
f6bcfd97 | 255 | sm_lastError = wxPRINTER_CANCELLED; |
7bcb11d3 JS |
256 | break; |
257 | } | |
70846f0a VZ |
258 | |
259 | dc->StartPage(); | |
260 | bool cont = printout->OnPrintPage(pn); | |
261 | dc->EndPage(); | |
262 | ||
263 | if ( !cont ) | |
f6bcfd97 BP |
264 | { |
265 | sm_lastError = wxPRINTER_CANCELLED; | |
70846f0a | 266 | break; |
f6bcfd97 | 267 | } |
7bcb11d3 | 268 | } |
70846f0a | 269 | |
7bcb11d3 JS |
270 | printout->OnEndDocument(); |
271 | } | |
103aec29 | 272 | |
7bcb11d3 | 273 | printout->OnEndPrinting(); |
103aec29 | 274 | |
7bcb11d3 | 275 | if (sm_abortWindow) |
2bda0e17 | 276 | { |
e41e579f | 277 | sm_abortWindow->Show(false); |
7bcb11d3 JS |
278 | delete sm_abortWindow; |
279 | sm_abortWindow = NULL; | |
2bda0e17 | 280 | } |
103aec29 | 281 | |
7bcb11d3 | 282 | delete dc; |
103aec29 | 283 | |
e71693c3 | 284 | return sm_lastError == wxPRINTER_NO_ERROR; |
7bcb11d3 | 285 | } |
2bda0e17 | 286 | |
f415cab9 | 287 | wxDC *wxWindowsPrinter::PrintDialog(wxWindow *parent) |
7bcb11d3 | 288 | { |
f415cab9 | 289 | wxDC *dc = (wxPrinterDC*) NULL; |
2bda0e17 | 290 | |
f415cab9 | 291 | wxWindowsPrintDialog dialog(parent, & m_printDialogData); |
7bcb11d3 | 292 | int ret = dialog.ShowModal(); |
2bda0e17 | 293 | |
7bcb11d3 JS |
294 | if (ret == wxID_OK) |
295 | { | |
296 | dc = dialog.GetPrintDC(); | |
297 | m_printDialogData = dialog.GetPrintDialogData(); | |
33ac7e6f | 298 | if (dc == NULL) |
f6bcfd97 BP |
299 | sm_lastError = wxPRINTER_ERROR; |
300 | else | |
301 | sm_lastError = wxPRINTER_NO_ERROR; | |
7bcb11d3 | 302 | } |
f6bcfd97 BP |
303 | else |
304 | sm_lastError = wxPRINTER_CANCELLED; | |
2bda0e17 | 305 | |
7bcb11d3 | 306 | return dc; |
2bda0e17 KB |
307 | } |
308 | ||
a12f001e | 309 | bool wxWindowsPrinter::Setup(wxWindow *WXUNUSED(parent)) |
2bda0e17 | 310 | { |
b45dfd0a RR |
311 | #if 0 |
312 | // We no longer expose that dialog | |
7bcb11d3 | 313 | wxPrintDialog dialog(parent, & m_printDialogData); |
e41e579f | 314 | dialog.GetPrintDialogData().SetSetupDialog(true); |
7bcb11d3 JS |
315 | |
316 | int ret = dialog.ShowModal(); | |
317 | ||
318 | if (ret == wxID_OK) | |
319 | { | |
320 | m_printDialogData = dialog.GetPrintDialogData(); | |
321 | } | |
322 | ||
323 | return (ret == wxID_OK); | |
b45dfd0a | 324 | #else |
3950f9e1 | 325 | return false; |
b45dfd0a | 326 | #endif |
2bda0e17 KB |
327 | } |
328 | ||
329 | /* | |
7bcb11d3 JS |
330 | * Print preview |
331 | */ | |
2bda0e17 | 332 | |
103aec29 VZ |
333 | wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, |
334 | wxPrintout *printoutForPrinting, | |
335 | wxPrintDialogData *data) | |
336 | : wxPrintPreviewBase(printout, printoutForPrinting, data) | |
2bda0e17 | 337 | { |
25a3fca2 VS |
338 | #if wxUSE_HIGH_QUALITY_PREVIEW |
339 | m_hqPreviewFailed = false; | |
340 | #endif | |
7bcb11d3 | 341 | DetermineScaling(); |
2bda0e17 KB |
342 | } |
343 | ||
103aec29 VZ |
344 | wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, |
345 | wxPrintout *printoutForPrinting, | |
346 | wxPrintData *data) | |
347 | : wxPrintPreviewBase(printout, printoutForPrinting, data) | |
348 | { | |
25a3fca2 VS |
349 | #if wxUSE_HIGH_QUALITY_PREVIEW |
350 | m_hqPreviewFailed = false; | |
351 | #endif | |
103aec29 VZ |
352 | DetermineScaling(); |
353 | } | |
354 | ||
355 | wxWindowsPrintPreview::~wxWindowsPrintPreview() | |
2bda0e17 KB |
356 | { |
357 | } | |
358 | ||
359 | bool wxWindowsPrintPreview::Print(bool interactive) | |
360 | { | |
7bcb11d3 | 361 | if (!m_printPrintout) |
e41e579f | 362 | return false; |
7bcb11d3 JS |
363 | wxWindowsPrinter printer(&m_printDialogData); |
364 | return printer.Print(m_previewFrame, m_printPrintout, interactive); | |
2bda0e17 KB |
365 | } |
366 | ||
103aec29 | 367 | void wxWindowsPrintPreview::DetermineScaling() |
2bda0e17 | 368 | { |
f2a59080 | 369 | ScreenHDC dc; |
2bda0e17 KB |
370 | int logPPIScreenX = ::GetDeviceCaps(dc, LOGPIXELSX); |
371 | int logPPIScreenY = ::GetDeviceCaps(dc, LOGPIXELSY); | |
34da0970 | 372 | m_previewPrintout->SetPPIScreen(logPPIScreenX, logPPIScreenY); |
103aec29 | 373 | |
2bda0e17 | 374 | // Get a device context for the currently selected printer |
7bcb11d3 | 375 | wxPrinterDC printerDC(m_printDialogData.GetPrintData()); |
103aec29 | 376 | |
f415cab9 JS |
377 | int printerWidthMM; |
378 | int printerHeightMM; | |
379 | int printerXRes; | |
380 | int printerYRes; | |
381 | int logPPIPrinterX; | |
382 | int logPPIPrinterY; | |
383 | ||
f4322df6 | 384 | wxRect paperRect; |
103aec29 | 385 | |
888dde65 | 386 | if ( printerDC.IsOk() ) |
2bda0e17 | 387 | { |
888dde65 RR |
388 | wxPrinterDCImpl *impl = (wxPrinterDCImpl*) printerDC.GetImpl(); |
389 | HDC dc = GetHdcOf(*impl); | |
f415cab9 JS |
390 | printerWidthMM = ::GetDeviceCaps(dc, HORZSIZE); |
391 | printerHeightMM = ::GetDeviceCaps(dc, VERTSIZE); | |
f4fefc23 VZ |
392 | printerXRes = ::GetDeviceCaps(dc, HORZRES); |
393 | printerYRes = ::GetDeviceCaps(dc, VERTRES); | |
f415cab9 JS |
394 | logPPIPrinterX = ::GetDeviceCaps(dc, LOGPIXELSX); |
395 | logPPIPrinterY = ::GetDeviceCaps(dc, LOGPIXELSY); | |
103aec29 | 396 | |
f4322df6 | 397 | paperRect = printerDC.GetPaperRect(); |
103aec29 | 398 | |
f2a59080 VZ |
399 | if ( logPPIPrinterX == 0 || |
400 | logPPIPrinterY == 0 || | |
f415cab9 JS |
401 | printerWidthMM == 0 || |
402 | printerHeightMM == 0 ) | |
f2a59080 | 403 | { |
e41e579f | 404 | m_isOk = false; |
f2a59080 | 405 | } |
2bda0e17 KB |
406 | } |
407 | else | |
f2a59080 | 408 | { |
f415cab9 JS |
409 | // use some defaults |
410 | printerWidthMM = 150; | |
411 | printerHeightMM = 250; | |
412 | printerXRes = 1500; | |
413 | printerYRes = 2500; | |
414 | logPPIPrinterX = 600; | |
415 | logPPIPrinterY = 600; | |
416 | ||
f4322df6 | 417 | paperRect = wxRect(0, 0, printerXRes, printerYRes); |
e41e579f | 418 | m_isOk = false; |
f2a59080 | 419 | } |
34da0970 JS |
420 | m_pageWidth = printerXRes; |
421 | m_pageHeight = printerYRes; | |
f415cab9 JS |
422 | m_previewPrintout->SetPageSizePixels(printerXRes, printerYRes); |
423 | m_previewPrintout->SetPageSizeMM(printerWidthMM, printerHeightMM); | |
424 | m_previewPrintout->SetPaperRectPixels(paperRect); | |
425 | m_previewPrintout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY); | |
103aec29 | 426 | |
2bda0e17 | 427 | // At 100%, the page should look about page-size on the screen. |
f415cab9 JS |
428 | m_previewScaleX = float(logPPIScreenX) / logPPIPrinterX; |
429 | m_previewScaleY = float(logPPIScreenY) / logPPIPrinterY; | |
2bda0e17 KB |
430 | } |
431 | ||
25a3fca2 VS |
432 | |
433 | #if wxUSE_HIGH_QUALITY_PREVIEW | |
434 | ||
435 | // The preview, as implemented in wxPrintPreviewBase (and as used prior to wx3) | |
436 | // is inexact: it uses screen DC, which has much lower resolution and has | |
437 | // other properties different from printer DC, so the preview is not quite | |
438 | // right. | |
439 | // | |
440 | // To make matters worse, if the application depends heavily on GetTextExtent() | |
441 | // or does text layout itself, the output in preview and on paper can be very | |
442 | // different. In particular, wxHtmlEasyPrinting is affected and the preview | |
443 | // can be easily off by several pages. | |
444 | // | |
445 | // To fix this, we attempt to render the preview into high-resolution bitmap | |
446 | // using DC with same resolution etc. as the printer DC. This takes lot of | |
447 | // memory, so the code is more complicated than it could be, but the results | |
448 | // are much better. | |
449 | // | |
450 | // Finally, this code is specific to wxMSW, because it doesn't make sense to | |
451 | // bother with it on other platforms. Both OSX and modern GNOME/GTK+ | |
452 | // environments have builtin accurate preview (that applications should use | |
453 | // instead) and the differences between screen and printer DC in wxGTK are so | |
454 | // large than this trick doesn't help at all. | |
455 | ||
456 | namespace | |
457 | { | |
458 | ||
459 | // If there's not enough memory, we need to render the preview in parts. | |
460 | // Unfortunately we cannot simply use wxMemoryDC, because it reports its size | |
461 | // as bitmap's size, and we need to use smaller bitmap while still reporting | |
462 | // original ("correct") DC size, because printing code frequently uses | |
463 | // GetSize() to determine scaling factor. This DC class handles this. | |
464 | ||
465 | class PageFragmentDCImpl : public wxMemoryDCImpl | |
466 | { | |
467 | public: | |
373c713a VS |
468 | PageFragmentDCImpl(wxMemoryDC *owner, wxDC *printer, |
469 | const wxPoint& offset, | |
470 | const wxSize& fullSize) | |
25a3fca2 | 471 | : wxMemoryDCImpl(owner, printer), |
373c713a VS |
472 | m_offset(offset), |
473 | m_fullSize(fullSize) | |
25a3fca2 VS |
474 | { |
475 | SetDeviceOrigin(0, 0); | |
476 | } | |
477 | ||
478 | virtual void SetDeviceOrigin(wxCoord x, wxCoord y) | |
479 | { | |
373c713a | 480 | wxMemoryDCImpl::SetDeviceOrigin(x - m_offset.x, y - m_offset.y); |
25a3fca2 VS |
481 | } |
482 | ||
483 | virtual void DoGetDeviceOrigin(wxCoord *x, wxCoord *y) const | |
484 | { | |
485 | wxMemoryDCImpl::DoGetDeviceOrigin(x, y); | |
373c713a VS |
486 | if ( x ) *x += m_offset.x; |
487 | if ( x ) *y += m_offset.y; | |
25a3fca2 VS |
488 | } |
489 | ||
490 | virtual void DoGetSize(int *width, int *height) const | |
491 | { | |
492 | if ( width ) | |
373c713a | 493 | *width = m_fullSize.x; |
25a3fca2 | 494 | if ( height ) |
373c713a | 495 | *height = m_fullSize.y; |
25a3fca2 VS |
496 | } |
497 | ||
498 | private: | |
373c713a VS |
499 | wxPoint m_offset; |
500 | wxSize m_fullSize; | |
25a3fca2 VS |
501 | }; |
502 | ||
503 | class PageFragmentDC : public wxDC | |
504 | { | |
505 | public: | |
373c713a VS |
506 | PageFragmentDC(wxDC* printer, wxBitmap& bmp, |
507 | const wxPoint& offset, | |
508 | const wxSize& fullSize) | |
509 | : wxDC(new PageFragmentDCImpl((wxMemoryDC*)this, printer, offset, fullSize)) | |
25a3fca2 VS |
510 | { |
511 | wx_static_cast(PageFragmentDCImpl*, m_pimpl)->DoSelect(bmp); | |
512 | } | |
513 | }; | |
514 | ||
515 | // estimate how big chunks we can render, given available RAM | |
516 | long ComputeFragmentSize(long printerDepth, | |
517 | long width, | |
518 | long height) | |
519 | { | |
520 | // Compute the amount of memory needed to generate the preview. | |
521 | // Memory requirements of RenderPageFragment() are as follows: | |
522 | // | |
523 | // (memory DC - always) | |
524 | // width * height * printerDepth/8 | |
525 | // (wxImage + wxDIB instance) | |
526 | // width * height * (3 + 4) | |
527 | // (this could be reduced to *3 if using wxGraphicsContext) | |
528 | // | |
529 | // So, given amount of memory M, we can render at most | |
530 | // | |
531 | // height = M / (width * (printerDepth/8 + F)) | |
532 | // | |
533 | // where F is 3 or 7 depending on whether wxGraphicsContext is used or not | |
534 | ||
535 | wxMemorySize memAvail = wxGetFreeMemory(); | |
536 | if ( memAvail == -1 ) | |
537 | { | |
538 | // we don't know; 10meg shouldn't be a problem hopefully | |
539 | memAvail = 10000000; | |
540 | } | |
541 | else | |
542 | { | |
543 | // limit ourselves to half of available RAM to have a margin for other | |
544 | // apps, for our rendering code, and for miscalculations | |
545 | memAvail /= 2; | |
546 | } | |
547 | ||
548 | const float perPixel = float(printerDepth)/8 + (3 + 4); | |
549 | ||
550 | const long perLine = long(width * perPixel); | |
551 | const long maxstep = (memAvail / perLine).GetValue(); | |
552 | const long step = wxMin(height, maxstep); | |
553 | ||
554 | wxLogTrace("printing", | |
555 | "using %liMB of RAM (%li lines) for preview, %li %lipx fragments", | |
556 | long((memAvail >> 20).GetValue()), | |
557 | maxstep, | |
558 | (height+step-1) / step, | |
559 | step); | |
560 | ||
561 | return step; | |
562 | } | |
563 | ||
564 | } // anonymous namespace | |
565 | ||
566 | ||
567 | bool wxWindowsPrintPreview::RenderPageFragment(float scaleX, float scaleY, | |
568 | int *nextFinalLine, | |
569 | wxPrinterDC& printer, | |
570 | wxMemoryDC& finalDC, | |
571 | const wxRect& rect, | |
572 | int pageNum) | |
573 | { | |
574 | // compute 'rect' equivalent in the small final bitmap: | |
575 | const wxRect smallRect(wxPoint(0, *nextFinalLine), | |
576 | wxPoint(int(rect.GetRight() * scaleX), | |
577 | int(rect.GetBottom() * scaleY))); | |
578 | wxLogTrace("printing", | |
579 | "rendering fragment of page %i: [%i,%i,%i,%i] scaled down to [%i,%i,%i,%i]", | |
580 | pageNum, | |
581 | rect.x, rect.y, rect.GetRight(), rect.GetBottom(), | |
582 | smallRect.x, smallRect.y, smallRect.GetRight(), smallRect.GetBottom() | |
583 | ); | |
584 | ||
585 | // create DC and bitmap compatible with printer DC: | |
586 | wxBitmap large(rect.width, rect.height, printer); | |
587 | if ( !large.IsOk() ) | |
588 | return false; | |
589 | ||
590 | // render part of the page into it: | |
591 | { | |
373c713a VS |
592 | PageFragmentDC memoryDC(&printer, large, |
593 | rect.GetPosition(), | |
594 | wxSize(m_pageWidth, m_pageHeight)); | |
25a3fca2 VS |
595 | if ( !memoryDC.IsOk() ) |
596 | return false; | |
597 | ||
598 | memoryDC.Clear(); | |
599 | ||
600 | if ( !RenderPageIntoDC(memoryDC, pageNum) ) | |
601 | return false; | |
602 | } // release bitmap from memoryDC | |
603 | ||
604 | // now scale the rendered part down and blit it into final output: | |
605 | ||
606 | wxImage img; | |
607 | { | |
608 | wxDIB dib(large); | |
609 | if ( !dib.IsOk() ) | |
610 | return false; | |
611 | large = wxNullBitmap; // free memory a.s.a.p. | |
612 | img = dib.ConvertToImage(); | |
613 | } // free the DIB now that it's no longer needed, too | |
614 | ||
615 | if ( !img.IsOk() ) | |
616 | return false; | |
617 | ||
618 | img.Rescale(smallRect.width, smallRect.height, wxIMAGE_QUALITY_HIGH); | |
619 | if ( !img.IsOk() ) | |
620 | return false; | |
621 | ||
622 | wxBitmap bmp(img); | |
623 | if ( !bmp.IsOk() ) | |
624 | return false; | |
625 | ||
626 | img = wxNullImage; | |
627 | finalDC.DrawBitmap(bmp, smallRect.x, smallRect.y); | |
628 | if ( bmp.IsOk() ) | |
629 | { | |
630 | *nextFinalLine += smallRect.height; | |
631 | return true; | |
632 | } | |
633 | ||
634 | return false; | |
635 | } | |
636 | ||
637 | bool wxWindowsPrintPreview::RenderPageIntoBitmapHQ(wxBitmap& bmp, int pageNum) | |
638 | { | |
639 | wxLogTrace("printing", "rendering HQ preview of page %i", pageNum); | |
640 | ||
641 | wxPrinterDC printerDC(m_printDialogData.GetPrintData()); | |
642 | if ( !printerDC.IsOk() ) | |
643 | return false; | |
644 | ||
645 | // compute scale factor | |
646 | const float scaleX = float(bmp.GetWidth()) / float(m_pageWidth); | |
647 | const float scaleY = float(bmp.GetHeight()) / float(m_pageHeight); | |
648 | ||
649 | wxMemoryDC bmpDC; | |
650 | bmpDC.SelectObject(bmp); | |
651 | bmpDC.Clear(); | |
652 | ||
653 | const int initialStep = ComputeFragmentSize(printerDC.GetDepth(), | |
654 | m_pageWidth, m_pageHeight); | |
655 | ||
656 | wxRect todo(0, 0, m_pageWidth, initialStep); // rect to render | |
657 | int nextFinalLine = 0; // first not-yet-rendered output line | |
658 | ||
659 | while ( todo.y < m_pageHeight ) | |
660 | { | |
661 | todo.SetBottom(wxMin(todo.GetBottom(), m_pageHeight - 1)); | |
662 | ||
663 | if ( !RenderPageFragment(scaleX, scaleY, | |
664 | &nextFinalLine, | |
665 | printerDC, | |
666 | bmpDC, | |
667 | todo, | |
668 | pageNum) ) | |
669 | { | |
670 | if ( todo.height < 20 ) | |
671 | { | |
672 | // something is very wrong if we can't render even at this | |
673 | // slow space, let's bail out and fall back to low quality | |
674 | // preview | |
675 | wxLogTrace("printing", | |
676 | "it seems that HQ preview doesn't work at all"); | |
677 | return false; | |
678 | } | |
679 | ||
680 | // it's possible our memory calculation was off, or conditions | |
681 | // changed, or there's not enough _bitmap_ resources; try if using | |
682 | // smaller bitmap would help: | |
683 | todo.height /= 2; | |
684 | ||
685 | wxLogTrace("printing", | |
686 | "preview of fragment failed, reducing height to %ipx", | |
687 | todo.height); | |
688 | ||
689 | continue; // retry at the same position again | |
690 | } | |
691 | ||
692 | // move to the next segment | |
693 | todo.Offset(0, todo.height); | |
694 | } | |
695 | ||
696 | return true; | |
697 | } | |
698 | ||
699 | bool wxWindowsPrintPreview::RenderPageIntoBitmap(wxBitmap& bmp, int pageNum) | |
700 | { | |
701 | // try high quality rendering first: | |
702 | if ( !m_hqPreviewFailed ) | |
703 | { | |
704 | if ( RenderPageIntoBitmapHQ(bmp, pageNum) ) | |
705 | { | |
706 | return true; | |
707 | } | |
708 | else | |
709 | { | |
710 | wxLogTrace("printing", | |
711 | "high-quality preview failed, falling back to normal"); | |
712 | m_hqPreviewFailed = true; // don't bother re-trying | |
713 | } | |
714 | } | |
715 | ||
716 | // if it fails, use the generic method that is less resource intensive, | |
717 | // but inexact | |
718 | return wxPrintPreviewBase::RenderPageIntoBitmap(bmp, pageNum); | |
719 | } | |
720 | ||
721 | #endif // wxUSE_HIGH_QUALITY_PREVIEW | |
722 | ||
2bda0e17 KB |
723 | /**************************************************************************** |
724 | ||
7bcb11d3 | 725 | FUNCTION: wxAbortProc() |
103aec29 | 726 | |
2bda0e17 | 727 | PURPOSE: Processes messages for the Abort Dialog box |
103aec29 | 728 | |
2bda0e17 KB |
729 | ****************************************************************************/ |
730 | ||
731 | LONG APIENTRY _EXPORT wxAbortProc(HDC WXUNUSED(hPr), int WXUNUSED(Code)) | |
732 | { | |
733 | MSG msg; | |
103aec29 | 734 | |
34da0970 | 735 | if (!wxPrinterBase::sm_abortWindow) /* If the abort dialog isn't up yet */ |
2bda0e17 | 736 | return(TRUE); |
103aec29 | 737 | |
2bda0e17 | 738 | /* Process messages intended for the abort dialog box */ |
103aec29 | 739 | |
078cf5cb | 740 | while (!wxPrinterBase::sm_abortIt && ::PeekMessage(&msg, 0, 0, 0, TRUE)) |
34da0970 | 741 | if (!IsDialogMessage((HWND) wxPrinterBase::sm_abortWindow->GetHWND(), &msg)) { |
2bda0e17 KB |
742 | TranslateMessage(&msg); |
743 | DispatchMessage(&msg); | |
744 | } | |
103aec29 | 745 | |
e71693c3 | 746 | /* bAbort is TRUE (return is FALSE) if the user has aborted */ |
103aec29 | 747 | |
e71693c3 | 748 | return !wxPrinterBase::sm_abortIt; |
2bda0e17 KB |
749 | } |
750 | ||
f6bcfd97 BP |
751 | #endif |
752 | // wxUSE_PRINTING_ARCHITECTURE |