]>
Commit | Line | Data |
---|---|---|
1 | ///////////////////////////////////////////////////////////////////////////// | |
2 | // Name: src/msw/printwin.cpp | |
3 | // Purpose: wxWindowsPrinter framework | |
4 | // Author: Julian Smart | |
5 | // Modified by: | |
6 | // Created: 04/01/98 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) Julian Smart | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | // =========================================================================== | |
13 | // declarations | |
14 | // =========================================================================== | |
15 | ||
16 | // --------------------------------------------------------------------------- | |
17 | // headers | |
18 | // --------------------------------------------------------------------------- | |
19 | ||
20 | // For compilers that support precompilation, includes "wx.h". | |
21 | #include "wx/wxprec.h" | |
22 | ||
23 | #ifdef __BORLANDC__ | |
24 | #pragma hdrstop | |
25 | #endif | |
26 | ||
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) | |
30 | ||
31 | #ifndef WX_PRECOMP | |
32 | #include "wx/msw/wrapcdlg.h" | |
33 | #include "wx/window.h" | |
34 | #include "wx/msw/private.h" | |
35 | #include "wx/utils.h" | |
36 | #include "wx/dc.h" | |
37 | #include "wx/app.h" | |
38 | #include "wx/msgdlg.h" | |
39 | #include "wx/intl.h" | |
40 | #include "wx/log.h" | |
41 | #include "wx/dcprint.h" | |
42 | #include "wx/dcmemory.h" | |
43 | #include "wx/image.h" | |
44 | #endif | |
45 | ||
46 | #include "wx/msw/dib.h" | |
47 | #include "wx/msw/dcmemory.h" | |
48 | #include "wx/msw/printwin.h" | |
49 | #include "wx/msw/printdlg.h" | |
50 | #include "wx/msw/private.h" | |
51 | #include "wx/msw/dcprint.h" | |
52 | ||
53 | #include <stdlib.h> | |
54 | ||
55 | #ifndef __WIN32__ | |
56 | #include <print.h> | |
57 | #endif | |
58 | ||
59 | // --------------------------------------------------------------------------- | |
60 | // private functions | |
61 | // --------------------------------------------------------------------------- | |
62 | ||
63 | LONG APIENTRY _EXPORT wxAbortProc(HDC hPr, int Code); | |
64 | ||
65 | // --------------------------------------------------------------------------- | |
66 | // wxWin macros | |
67 | // --------------------------------------------------------------------------- | |
68 | ||
69 | IMPLEMENT_DYNAMIC_CLASS(wxWindowsPrinter, wxPrinterBase) | |
70 | IMPLEMENT_CLASS(wxWindowsPrintPreview, wxPrintPreviewBase) | |
71 | ||
72 | // =========================================================================== | |
73 | // implementation | |
74 | // =========================================================================== | |
75 | ||
76 | // --------------------------------------------------------------------------- | |
77 | // Printer | |
78 | // --------------------------------------------------------------------------- | |
79 | ||
80 | wxWindowsPrinter::wxWindowsPrinter(wxPrintDialogData *data) | |
81 | : wxPrinterBase(data) | |
82 | { | |
83 | m_lpAbortProc = (WXFARPROC)wxAbortProc; | |
84 | } | |
85 | ||
86 | wxWindowsPrinter::~wxWindowsPrinter() | |
87 | { | |
88 | // avoids warning about statement with no effect (FreeProcInstance | |
89 | // doesn't do anything under Win32) | |
90 | #if !defined(__WIN32__) && !defined(__NT__) | |
91 | FreeProcInstance((FARPROC) m_lpAbortProc); | |
92 | #endif | |
93 | } | |
94 | ||
95 | bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt) | |
96 | { | |
97 | sm_abortIt = false; | |
98 | sm_abortWindow = NULL; | |
99 | ||
100 | if (!printout) | |
101 | { | |
102 | sm_lastError = wxPRINTER_ERROR; | |
103 | return false; | |
104 | } | |
105 | ||
106 | printout->SetIsPreview(false); | |
107 | ||
108 | if (m_printDialogData.GetMinPage() < 1) | |
109 | m_printDialogData.SetMinPage(1); | |
110 | if (m_printDialogData.GetMaxPage() < 1) | |
111 | m_printDialogData.SetMaxPage(9999); | |
112 | ||
113 | // Create a suitable device context | |
114 | wxPrinterDC *dc wxDUMMY_INITIALIZE(NULL); | |
115 | if (prompt) | |
116 | { | |
117 | dc = wxDynamicCast(PrintDialog(parent), wxPrinterDC); | |
118 | if (!dc) | |
119 | return false; | |
120 | } | |
121 | else | |
122 | { | |
123 | dc = new wxPrinterDC(m_printDialogData.GetPrintData()); | |
124 | } | |
125 | ||
126 | // May have pressed cancel. | |
127 | if (!dc || !dc->IsOk()) | |
128 | { | |
129 | if (dc) delete dc; | |
130 | return false; | |
131 | } | |
132 | ||
133 | wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl(); | |
134 | ||
135 | HDC hdc = ::GetDC(NULL); | |
136 | int logPPIScreenX = ::GetDeviceCaps(hdc, LOGPIXELSX); | |
137 | int logPPIScreenY = ::GetDeviceCaps(hdc, LOGPIXELSY); | |
138 | ::ReleaseDC(NULL, hdc); | |
139 | ||
140 | int logPPIPrinterX = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSX); | |
141 | int logPPIPrinterY = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSY); | |
142 | if (logPPIPrinterX == 0 || logPPIPrinterY == 0) | |
143 | { | |
144 | delete dc; | |
145 | sm_lastError = wxPRINTER_ERROR; | |
146 | return false; | |
147 | } | |
148 | ||
149 | printout->SetPPIScreen(logPPIScreenX, logPPIScreenY); | |
150 | printout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY); | |
151 | ||
152 | // Set printout parameters | |
153 | printout->SetDC(dc); | |
154 | ||
155 | int w, h; | |
156 | dc->GetSize(&w, &h); | |
157 | printout->SetPageSizePixels((int)w, (int)h); | |
158 | printout->SetPaperRectPixels(dc->GetPaperRect()); | |
159 | ||
160 | dc->GetSizeMM(&w, &h); | |
161 | printout->SetPageSizeMM((int)w, (int)h); | |
162 | ||
163 | // Create an abort window | |
164 | wxBusyCursor busyCursor; | |
165 | ||
166 | printout->OnPreparePrinting(); | |
167 | ||
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; | |
176 | return false; | |
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 | ||
184 | wxWindow *win = CreateAbortWindow(parent, printout); | |
185 | wxYield(); | |
186 | ||
187 | #if defined(__WATCOMC__) || defined(__BORLANDC__) || defined(__GNUWIN32__) || !defined(__WIN32__) | |
188 | #ifdef STRICT | |
189 | ::SetAbortProc((HDC) impl->GetHDC(), (ABORTPROC) m_lpAbortProc); | |
190 | #else | |
191 | ::SetAbortProc((HDC) impl->GetHDC(), (FARPROC) m_lpAbortProc); | |
192 | #endif | |
193 | #else | |
194 | ::SetAbortProc((HDC) impl->GetHDC(), (int (_stdcall *) | |
195 | // cast it to right type only if required | |
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 | |
198 | #ifdef STRICT | |
199 | (HDC, int) | |
200 | #else | |
201 | () | |
202 | #endif | |
203 | )m_lpAbortProc); | |
204 | #endif | |
205 | ||
206 | if (!win) | |
207 | { | |
208 | wxLogDebug(wxT("Could not create an abort dialog.")); | |
209 | sm_lastError = wxPRINTER_ERROR; | |
210 | ||
211 | delete dc; | |
212 | return false; | |
213 | } | |
214 | sm_abortWindow = win; | |
215 | sm_abortWindow->Show(); | |
216 | wxSafeYield(); | |
217 | ||
218 | printout->OnBeginPrinting(); | |
219 | ||
220 | sm_lastError = wxPRINTER_NO_ERROR; | |
221 | ||
222 | int minPageNum = minPage, maxPageNum = maxPage; | |
223 | ||
224 | if ( !m_printDialogData.GetAllPages() ) | |
225 | { | |
226 | minPageNum = m_printDialogData.GetFromPage(); | |
227 | maxPageNum = m_printDialogData.GetToPage(); | |
228 | } | |
229 | ||
230 | int copyCount; | |
231 | for ( copyCount = 1; | |
232 | copyCount <= m_printDialogData.GetNoCopies(); | |
233 | copyCount++ ) | |
234 | { | |
235 | if ( !printout->OnBeginDocument(minPageNum, maxPageNum) ) | |
236 | { | |
237 | wxLogError(_("Could not start printing.")); | |
238 | sm_lastError = wxPRINTER_ERROR; | |
239 | break; | |
240 | } | |
241 | if (sm_abortIt) | |
242 | { | |
243 | sm_lastError = wxPRINTER_CANCELLED; | |
244 | break; | |
245 | } | |
246 | ||
247 | int pn; | |
248 | ||
249 | for ( pn = minPageNum; | |
250 | pn <= maxPageNum && printout->HasPage(pn); | |
251 | pn++ ) | |
252 | { | |
253 | if ( sm_abortIt ) | |
254 | { | |
255 | sm_lastError = wxPRINTER_CANCELLED; | |
256 | break; | |
257 | } | |
258 | ||
259 | dc->StartPage(); | |
260 | bool cont = printout->OnPrintPage(pn); | |
261 | dc->EndPage(); | |
262 | ||
263 | if ( !cont ) | |
264 | { | |
265 | sm_lastError = wxPRINTER_CANCELLED; | |
266 | break; | |
267 | } | |
268 | } | |
269 | ||
270 | printout->OnEndDocument(); | |
271 | } | |
272 | ||
273 | printout->OnEndPrinting(); | |
274 | ||
275 | if (sm_abortWindow) | |
276 | { | |
277 | sm_abortWindow->Show(false); | |
278 | delete sm_abortWindow; | |
279 | sm_abortWindow = NULL; | |
280 | } | |
281 | ||
282 | delete dc; | |
283 | ||
284 | return sm_lastError == wxPRINTER_NO_ERROR; | |
285 | } | |
286 | ||
287 | wxDC *wxWindowsPrinter::PrintDialog(wxWindow *parent) | |
288 | { | |
289 | wxDC *dc = (wxPrinterDC*) NULL; | |
290 | ||
291 | wxWindowsPrintDialog dialog(parent, & m_printDialogData); | |
292 | int ret = dialog.ShowModal(); | |
293 | ||
294 | if (ret == wxID_OK) | |
295 | { | |
296 | dc = dialog.GetPrintDC(); | |
297 | m_printDialogData = dialog.GetPrintDialogData(); | |
298 | if (dc == NULL) | |
299 | sm_lastError = wxPRINTER_ERROR; | |
300 | else | |
301 | sm_lastError = wxPRINTER_NO_ERROR; | |
302 | } | |
303 | else | |
304 | sm_lastError = wxPRINTER_CANCELLED; | |
305 | ||
306 | return dc; | |
307 | } | |
308 | ||
309 | bool wxWindowsPrinter::Setup(wxWindow *WXUNUSED(parent)) | |
310 | { | |
311 | #if 0 | |
312 | // We no longer expose that dialog | |
313 | wxPrintDialog dialog(parent, & m_printDialogData); | |
314 | dialog.GetPrintDialogData().SetSetupDialog(true); | |
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); | |
324 | #else | |
325 | return false; | |
326 | #endif | |
327 | } | |
328 | ||
329 | /* | |
330 | * Print preview | |
331 | */ | |
332 | ||
333 | wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, | |
334 | wxPrintout *printoutForPrinting, | |
335 | wxPrintDialogData *data) | |
336 | : wxPrintPreviewBase(printout, printoutForPrinting, data) | |
337 | { | |
338 | #if wxUSE_HIGH_QUALITY_PREVIEW | |
339 | m_hqPreviewFailed = false; | |
340 | #endif | |
341 | DetermineScaling(); | |
342 | } | |
343 | ||
344 | wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, | |
345 | wxPrintout *printoutForPrinting, | |
346 | wxPrintData *data) | |
347 | : wxPrintPreviewBase(printout, printoutForPrinting, data) | |
348 | { | |
349 | #if wxUSE_HIGH_QUALITY_PREVIEW | |
350 | m_hqPreviewFailed = false; | |
351 | #endif | |
352 | DetermineScaling(); | |
353 | } | |
354 | ||
355 | wxWindowsPrintPreview::~wxWindowsPrintPreview() | |
356 | { | |
357 | } | |
358 | ||
359 | bool wxWindowsPrintPreview::Print(bool interactive) | |
360 | { | |
361 | if (!m_printPrintout) | |
362 | return false; | |
363 | wxWindowsPrinter printer(&m_printDialogData); | |
364 | return printer.Print(m_previewFrame, m_printPrintout, interactive); | |
365 | } | |
366 | ||
367 | void wxWindowsPrintPreview::DetermineScaling() | |
368 | { | |
369 | ScreenHDC dc; | |
370 | int logPPIScreenX = ::GetDeviceCaps(dc, LOGPIXELSX); | |
371 | int logPPIScreenY = ::GetDeviceCaps(dc, LOGPIXELSY); | |
372 | m_previewPrintout->SetPPIScreen(logPPIScreenX, logPPIScreenY); | |
373 | ||
374 | // Get a device context for the currently selected printer | |
375 | wxPrinterDC printerDC(m_printDialogData.GetPrintData()); | |
376 | ||
377 | int printerWidthMM; | |
378 | int printerHeightMM; | |
379 | int printerXRes; | |
380 | int printerYRes; | |
381 | int logPPIPrinterX; | |
382 | int logPPIPrinterY; | |
383 | ||
384 | wxRect paperRect; | |
385 | ||
386 | if ( printerDC.IsOk() ) | |
387 | { | |
388 | wxPrinterDCImpl *impl = (wxPrinterDCImpl*) printerDC.GetImpl(); | |
389 | HDC dc = GetHdcOf(*impl); | |
390 | printerWidthMM = ::GetDeviceCaps(dc, HORZSIZE); | |
391 | printerHeightMM = ::GetDeviceCaps(dc, VERTSIZE); | |
392 | printerXRes = ::GetDeviceCaps(dc, HORZRES); | |
393 | printerYRes = ::GetDeviceCaps(dc, VERTRES); | |
394 | logPPIPrinterX = ::GetDeviceCaps(dc, LOGPIXELSX); | |
395 | logPPIPrinterY = ::GetDeviceCaps(dc, LOGPIXELSY); | |
396 | ||
397 | paperRect = printerDC.GetPaperRect(); | |
398 | ||
399 | if ( logPPIPrinterX == 0 || | |
400 | logPPIPrinterY == 0 || | |
401 | printerWidthMM == 0 || | |
402 | printerHeightMM == 0 ) | |
403 | { | |
404 | m_isOk = false; | |
405 | } | |
406 | } | |
407 | else | |
408 | { | |
409 | // use some defaults | |
410 | printerWidthMM = 150; | |
411 | printerHeightMM = 250; | |
412 | printerXRes = 1500; | |
413 | printerYRes = 2500; | |
414 | logPPIPrinterX = 600; | |
415 | logPPIPrinterY = 600; | |
416 | ||
417 | paperRect = wxRect(0, 0, printerXRes, printerYRes); | |
418 | m_isOk = false; | |
419 | } | |
420 | m_pageWidth = printerXRes; | |
421 | m_pageHeight = printerYRes; | |
422 | m_previewPrintout->SetPageSizePixels(printerXRes, printerYRes); | |
423 | m_previewPrintout->SetPageSizeMM(printerWidthMM, printerHeightMM); | |
424 | m_previewPrintout->SetPaperRectPixels(paperRect); | |
425 | m_previewPrintout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY); | |
426 | ||
427 | // At 100%, the page should look about page-size on the screen. | |
428 | m_previewScaleX = float(logPPIScreenX) / logPPIPrinterX; | |
429 | m_previewScaleY = float(logPPIScreenY) / logPPIPrinterY; | |
430 | } | |
431 | ||
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: | |
468 | PageFragmentDCImpl(wxMemoryDC *owner, wxDC *printer, | |
469 | const wxPoint& offset, | |
470 | const wxSize& fullSize) | |
471 | : wxMemoryDCImpl(owner, printer), | |
472 | m_offset(offset), | |
473 | m_fullSize(fullSize) | |
474 | { | |
475 | SetDeviceOrigin(0, 0); | |
476 | } | |
477 | ||
478 | virtual void SetDeviceOrigin(wxCoord x, wxCoord y) | |
479 | { | |
480 | wxMemoryDCImpl::SetDeviceOrigin(x - m_offset.x, y - m_offset.y); | |
481 | } | |
482 | ||
483 | virtual void DoGetDeviceOrigin(wxCoord *x, wxCoord *y) const | |
484 | { | |
485 | wxMemoryDCImpl::DoGetDeviceOrigin(x, y); | |
486 | if ( x ) *x += m_offset.x; | |
487 | if ( x ) *y += m_offset.y; | |
488 | } | |
489 | ||
490 | virtual void DoGetSize(int *width, int *height) const | |
491 | { | |
492 | if ( width ) | |
493 | *width = m_fullSize.x; | |
494 | if ( height ) | |
495 | *height = m_fullSize.y; | |
496 | } | |
497 | ||
498 | private: | |
499 | wxPoint m_offset; | |
500 | wxSize m_fullSize; | |
501 | }; | |
502 | ||
503 | class PageFragmentDC : public wxDC | |
504 | { | |
505 | public: | |
506 | PageFragmentDC(wxDC* printer, wxBitmap& bmp, | |
507 | const wxPoint& offset, | |
508 | const wxSize& fullSize) | |
509 | : wxDC(new PageFragmentDCImpl((wxMemoryDC*)this, printer, offset, fullSize)) | |
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 | { | |
592 | PageFragmentDC memoryDC(&printer, large, | |
593 | rect.GetPosition(), | |
594 | wxSize(m_pageWidth, m_pageHeight)); | |
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 | ||
723 | /**************************************************************************** | |
724 | ||
725 | FUNCTION: wxAbortProc() | |
726 | ||
727 | PURPOSE: Processes messages for the Abort Dialog box | |
728 | ||
729 | ****************************************************************************/ | |
730 | ||
731 | LONG APIENTRY _EXPORT wxAbortProc(HDC WXUNUSED(hPr), int WXUNUSED(Code)) | |
732 | { | |
733 | MSG msg; | |
734 | ||
735 | if (!wxPrinterBase::sm_abortWindow) /* If the abort dialog isn't up yet */ | |
736 | return(TRUE); | |
737 | ||
738 | /* Process messages intended for the abort dialog box */ | |
739 | ||
740 | while (!wxPrinterBase::sm_abortIt && ::PeekMessage(&msg, 0, 0, 0, TRUE)) | |
741 | if (!IsDialogMessage((HWND) wxPrinterBase::sm_abortWindow->GetHWND(), &msg)) { | |
742 | TranslateMessage(&msg); | |
743 | DispatchMessage(&msg); | |
744 | } | |
745 | ||
746 | /* bAbort is TRUE (return is FALSE) if the user has aborted */ | |
747 | ||
748 | return !wxPrinterBase::sm_abortIt; | |
749 | } | |
750 | ||
751 | #endif | |
752 | // wxUSE_PRINTING_ARCHITECTURE |