]>
git.saurik.com Git - wxWidgets.git/blob - src/msw/printwin.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/printwin.cpp
3 // Purpose: wxWindowsPrinter framework
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
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)
32 #include "wx/msw/wrapcdlg.h"
33 #include "wx/window.h"
34 #include "wx/msw/private.h"
38 #include "wx/msgdlg.h"
41 #include "wx/dcprint.h"
42 #include "wx/dcmemory.h"
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"
59 // ---------------------------------------------------------------------------
61 // ---------------------------------------------------------------------------
63 LONG APIENTRY _EXPORT
wxAbortProc(HDC hPr
, int Code
);
65 // ---------------------------------------------------------------------------
67 // ---------------------------------------------------------------------------
69 IMPLEMENT_DYNAMIC_CLASS(wxWindowsPrinter
, wxPrinterBase
)
70 IMPLEMENT_CLASS(wxWindowsPrintPreview
, wxPrintPreviewBase
)
72 // ===========================================================================
74 // ===========================================================================
76 // ---------------------------------------------------------------------------
78 // ---------------------------------------------------------------------------
80 wxWindowsPrinter::wxWindowsPrinter(wxPrintDialogData
*data
)
83 m_lpAbortProc
= (WXFARPROC
)wxAbortProc
;
86 wxWindowsPrinter::~wxWindowsPrinter()
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
);
95 bool wxWindowsPrinter::Print(wxWindow
*parent
, wxPrintout
*printout
, bool prompt
)
98 sm_abortWindow
= NULL
;
102 sm_lastError
= wxPRINTER_ERROR
;
106 printout
->SetIsPreview(false);
108 if (m_printDialogData
.GetMinPage() < 1)
109 m_printDialogData
.SetMinPage(1);
110 if (m_printDialogData
.GetMaxPage() < 1)
111 m_printDialogData
.SetMaxPage(9999);
113 // Create a suitable device context
114 wxPrinterDC
*dc
wxDUMMY_INITIALIZE(NULL
);
117 dc
= wxDynamicCast(PrintDialog(parent
), wxPrinterDC
);
123 dc
= new wxPrinterDC(m_printDialogData
.GetPrintData());
126 // May have pressed cancel.
127 if (!dc
|| !dc
->IsOk())
133 wxPrinterDCImpl
*impl
= (wxPrinterDCImpl
*) dc
->GetImpl();
135 HDC hdc
= ::GetDC(NULL
);
136 int logPPIScreenX
= ::GetDeviceCaps(hdc
, LOGPIXELSX
);
137 int logPPIScreenY
= ::GetDeviceCaps(hdc
, LOGPIXELSY
);
138 ::ReleaseDC(NULL
, hdc
);
140 int logPPIPrinterX
= ::GetDeviceCaps((HDC
) impl
->GetHDC(), LOGPIXELSX
);
141 int logPPIPrinterY
= ::GetDeviceCaps((HDC
) impl
->GetHDC(), LOGPIXELSY
);
142 if (logPPIPrinterX
== 0 || logPPIPrinterY
== 0)
145 sm_lastError
= wxPRINTER_ERROR
;
149 printout
->SetPPIScreen(logPPIScreenX
, logPPIScreenY
);
150 printout
->SetPPIPrinter(logPPIPrinterX
, logPPIPrinterY
);
152 // Set printout parameters
157 printout
->SetPageSizePixels((int)w
, (int)h
);
158 printout
->SetPaperRectPixels(dc
->GetPaperRect());
160 dc
->GetSizeMM(&w
, &h
);
161 printout
->SetPageSizeMM((int)w
, (int)h
);
163 // Create an abort window
164 wxBusyCursor busyCursor
;
166 printout
->OnPreparePrinting();
168 // Get some parameters from the printout, if defined
169 int fromPage
, toPage
;
170 int minPage
, maxPage
;
171 printout
->GetPageInfo(&minPage
, &maxPage
, &fromPage
, &toPage
);
175 sm_lastError
= wxPRINTER_ERROR
;
179 // Only set min and max, because from and to have been
181 m_printDialogData
.SetMinPage(minPage
);
182 m_printDialogData
.SetMaxPage(maxPage
);
184 wxWindow
*win
= CreateAbortWindow(parent
, printout
);
187 #if defined(__WATCOMC__) || defined(__BORLANDC__) || defined(__GNUWIN32__) || !defined(__WIN32__)
189 ::SetAbortProc((HDC
) impl
->GetHDC(), (ABORTPROC
) m_lpAbortProc
);
191 ::SetAbortProc((HDC
) impl
->GetHDC(), (FARPROC
) m_lpAbortProc
);
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
208 wxLogDebug(wxT("Could not create an abort dialog."));
209 sm_lastError
= wxPRINTER_ERROR
;
214 sm_abortWindow
= win
;
215 sm_abortWindow
->Show();
218 printout
->OnBeginPrinting();
220 sm_lastError
= wxPRINTER_NO_ERROR
;
222 int minPageNum
= minPage
, maxPageNum
= maxPage
;
224 if ( !m_printDialogData
.GetAllPages() )
226 minPageNum
= m_printDialogData
.GetFromPage();
227 maxPageNum
= m_printDialogData
.GetToPage();
232 copyCount
<= m_printDialogData
.GetNoCopies();
235 if ( !printout
->OnBeginDocument(minPageNum
, maxPageNum
) )
237 wxLogError(_("Could not start printing."));
238 sm_lastError
= wxPRINTER_ERROR
;
243 sm_lastError
= wxPRINTER_CANCELLED
;
249 for ( pn
= minPageNum
;
250 pn
<= maxPageNum
&& printout
->HasPage(pn
);
255 sm_lastError
= wxPRINTER_CANCELLED
;
260 bool cont
= printout
->OnPrintPage(pn
);
265 sm_lastError
= wxPRINTER_CANCELLED
;
270 printout
->OnEndDocument();
273 printout
->OnEndPrinting();
277 sm_abortWindow
->Show(false);
278 delete sm_abortWindow
;
279 sm_abortWindow
= NULL
;
284 return sm_lastError
== wxPRINTER_NO_ERROR
;
287 wxDC
*wxWindowsPrinter::PrintDialog(wxWindow
*parent
)
289 wxDC
*dc
= (wxPrinterDC
*) NULL
;
291 wxWindowsPrintDialog
dialog(parent
, & m_printDialogData
);
292 int ret
= dialog
.ShowModal();
296 dc
= dialog
.GetPrintDC();
297 m_printDialogData
= dialog
.GetPrintDialogData();
299 sm_lastError
= wxPRINTER_ERROR
;
301 sm_lastError
= wxPRINTER_NO_ERROR
;
304 sm_lastError
= wxPRINTER_CANCELLED
;
309 bool wxWindowsPrinter::Setup(wxWindow
*WXUNUSED(parent
))
312 // We no longer expose that dialog
313 wxPrintDialog
dialog(parent
, & m_printDialogData
);
314 dialog
.GetPrintDialogData().SetSetupDialog(true);
316 int ret
= dialog
.ShowModal();
320 m_printDialogData
= dialog
.GetPrintDialogData();
323 return (ret
== wxID_OK
);
333 wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout
*printout
,
334 wxPrintout
*printoutForPrinting
,
335 wxPrintDialogData
*data
)
336 : wxPrintPreviewBase(printout
, printoutForPrinting
, data
)
338 #if wxUSE_HIGH_QUALITY_PREVIEW
339 m_hqPreviewFailed
= false;
344 wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout
*printout
,
345 wxPrintout
*printoutForPrinting
,
347 : wxPrintPreviewBase(printout
, printoutForPrinting
, data
)
349 #if wxUSE_HIGH_QUALITY_PREVIEW
350 m_hqPreviewFailed
= false;
355 wxWindowsPrintPreview::~wxWindowsPrintPreview()
359 bool wxWindowsPrintPreview::Print(bool interactive
)
361 if (!m_printPrintout
)
363 wxWindowsPrinter
printer(&m_printDialogData
);
364 return printer
.Print(m_previewFrame
, m_printPrintout
, interactive
);
367 void wxWindowsPrintPreview::DetermineScaling()
370 int logPPIScreenX
= ::GetDeviceCaps(dc
, LOGPIXELSX
);
371 int logPPIScreenY
= ::GetDeviceCaps(dc
, LOGPIXELSY
);
372 m_previewPrintout
->SetPPIScreen(logPPIScreenX
, logPPIScreenY
);
374 // Get a device context for the currently selected printer
375 wxPrinterDC
printerDC(m_printDialogData
.GetPrintData());
386 if ( printerDC
.IsOk() )
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
);
397 paperRect
= printerDC
.GetPaperRect();
399 if ( logPPIPrinterX
== 0 ||
400 logPPIPrinterY
== 0 ||
401 printerWidthMM
== 0 ||
402 printerHeightMM
== 0 )
410 printerWidthMM
= 150;
411 printerHeightMM
= 250;
414 logPPIPrinterX
= 600;
415 logPPIPrinterY
= 600;
417 paperRect
= wxRect(0, 0, printerXRes
, printerYRes
);
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
);
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
;
433 #if wxUSE_HIGH_QUALITY_PREVIEW
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
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.
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
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.
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.
465 class PageFragmentDCImpl
: public wxMemoryDCImpl
468 PageFragmentDCImpl(wxMemoryDC
*owner
, wxDC
*printer
, const wxRect
& rect
)
469 : wxMemoryDCImpl(owner
, printer
),
472 SetDeviceOrigin(0, 0);
475 virtual void SetDeviceOrigin(wxCoord x
, wxCoord y
)
477 wxMemoryDCImpl::SetDeviceOrigin(x
- m_rect
.x
, y
- m_rect
.y
);
480 virtual void DoGetDeviceOrigin(wxCoord
*x
, wxCoord
*y
) const
482 wxMemoryDCImpl::DoGetDeviceOrigin(x
, y
);
483 if ( x
) *x
+= m_rect
.x
;
484 if ( x
) *y
+= m_rect
.y
;
487 virtual void DoGetSize(int *width
, int *height
) const
490 *width
= m_rect
.width
;
492 *height
= m_rect
.height
;
499 class PageFragmentDC
: public wxDC
502 PageFragmentDC(wxDC
* printer
, wxBitmap
& bmp
, const wxRect
& rect
)
503 : wxDC(new PageFragmentDCImpl((wxMemoryDC
*)this, printer
, rect
))
505 wx_static_cast(PageFragmentDCImpl
*, m_pimpl
)->DoSelect(bmp
);
509 // estimate how big chunks we can render, given available RAM
510 long ComputeFragmentSize(long printerDepth
,
514 // Compute the amount of memory needed to generate the preview.
515 // Memory requirements of RenderPageFragment() are as follows:
517 // (memory DC - always)
518 // width * height * printerDepth/8
519 // (wxImage + wxDIB instance)
520 // width * height * (3 + 4)
521 // (this could be reduced to *3 if using wxGraphicsContext)
523 // So, given amount of memory M, we can render at most
525 // height = M / (width * (printerDepth/8 + F))
527 // where F is 3 or 7 depending on whether wxGraphicsContext is used or not
529 wxMemorySize memAvail
= wxGetFreeMemory();
530 if ( memAvail
== -1 )
532 // we don't know; 10meg shouldn't be a problem hopefully
537 // limit ourselves to half of available RAM to have a margin for other
538 // apps, for our rendering code, and for miscalculations
542 const float perPixel
= float(printerDepth
)/8 + (3 + 4);
544 const long perLine
= long(width
* perPixel
);
545 const long maxstep
= (memAvail
/ perLine
).GetValue();
546 const long step
= wxMin(height
, maxstep
);
548 wxLogTrace("printing",
549 "using %liMB of RAM (%li lines) for preview, %li %lipx fragments",
550 long((memAvail
>> 20).GetValue()),
552 (height
+step
-1) / step
,
558 } // anonymous namespace
561 bool wxWindowsPrintPreview::RenderPageFragment(float scaleX
, float scaleY
,
563 wxPrinterDC
& printer
,
568 // compute 'rect' equivalent in the small final bitmap:
569 const wxRect
smallRect(wxPoint(0, *nextFinalLine
),
570 wxPoint(int(rect
.GetRight() * scaleX
),
571 int(rect
.GetBottom() * scaleY
)));
572 wxLogTrace("printing",
573 "rendering fragment of page %i: [%i,%i,%i,%i] scaled down to [%i,%i,%i,%i]",
575 rect
.x
, rect
.y
, rect
.GetRight(), rect
.GetBottom(),
576 smallRect
.x
, smallRect
.y
, smallRect
.GetRight(), smallRect
.GetBottom()
579 // create DC and bitmap compatible with printer DC:
580 wxBitmap
large(rect
.width
, rect
.height
, printer
);
584 // render part of the page into it:
586 PageFragmentDC
memoryDC(&printer
, large
, rect
);
587 if ( !memoryDC
.IsOk() )
592 if ( !RenderPageIntoDC(memoryDC
, pageNum
) )
594 } // release bitmap from memoryDC
596 // now scale the rendered part down and blit it into final output:
603 large
= wxNullBitmap
; // free memory a.s.a.p.
604 img
= dib
.ConvertToImage();
605 } // free the DIB now that it's no longer needed, too
610 img
.Rescale(smallRect
.width
, smallRect
.height
, wxIMAGE_QUALITY_HIGH
);
619 finalDC
.DrawBitmap(bmp
, smallRect
.x
, smallRect
.y
);
622 *nextFinalLine
+= smallRect
.height
;
629 bool wxWindowsPrintPreview::RenderPageIntoBitmapHQ(wxBitmap
& bmp
, int pageNum
)
631 wxLogTrace("printing", "rendering HQ preview of page %i", pageNum
);
633 wxPrinterDC
printerDC(m_printDialogData
.GetPrintData());
634 if ( !printerDC
.IsOk() )
637 // compute scale factor
638 const float scaleX
= float(bmp
.GetWidth()) / float(m_pageWidth
);
639 const float scaleY
= float(bmp
.GetHeight()) / float(m_pageHeight
);
642 bmpDC
.SelectObject(bmp
);
645 const int initialStep
= ComputeFragmentSize(printerDC
.GetDepth(),
646 m_pageWidth
, m_pageHeight
);
648 wxRect
todo(0, 0, m_pageWidth
, initialStep
); // rect to render
649 int nextFinalLine
= 0; // first not-yet-rendered output line
651 while ( todo
.y
< m_pageHeight
)
653 todo
.SetBottom(wxMin(todo
.GetBottom(), m_pageHeight
- 1));
655 if ( !RenderPageFragment(scaleX
, scaleY
,
662 if ( todo
.height
< 20 )
664 // something is very wrong if we can't render even at this
665 // slow space, let's bail out and fall back to low quality
667 wxLogTrace("printing",
668 "it seems that HQ preview doesn't work at all");
672 // it's possible our memory calculation was off, or conditions
673 // changed, or there's not enough _bitmap_ resources; try if using
674 // smaller bitmap would help:
677 wxLogTrace("printing",
678 "preview of fragment failed, reducing height to %ipx",
681 continue; // retry at the same position again
684 // move to the next segment
685 todo
.Offset(0, todo
.height
);
691 bool wxWindowsPrintPreview::RenderPageIntoBitmap(wxBitmap
& bmp
, int pageNum
)
693 // try high quality rendering first:
694 if ( !m_hqPreviewFailed
)
696 if ( RenderPageIntoBitmapHQ(bmp
, pageNum
) )
702 wxLogTrace("printing",
703 "high-quality preview failed, falling back to normal");
704 m_hqPreviewFailed
= true; // don't bother re-trying
708 // if it fails, use the generic method that is less resource intensive,
710 return wxPrintPreviewBase::RenderPageIntoBitmap(bmp
, pageNum
);
713 #endif // wxUSE_HIGH_QUALITY_PREVIEW
715 /****************************************************************************
717 FUNCTION: wxAbortProc()
719 PURPOSE: Processes messages for the Abort Dialog box
721 ****************************************************************************/
723 LONG APIENTRY _EXPORT
wxAbortProc(HDC
WXUNUSED(hPr
), int WXUNUSED(Code
))
727 if (!wxPrinterBase::sm_abortWindow
) /* If the abort dialog isn't up yet */
730 /* Process messages intended for the abort dialog box */
732 while (!wxPrinterBase::sm_abortIt
&& ::PeekMessage(&msg
, 0, 0, 0, TRUE
))
733 if (!IsDialogMessage((HWND
) wxPrinterBase::sm_abortWindow
->GetHWND(), &msg
)) {
734 TranslateMessage(&msg
);
735 DispatchMessage(&msg
);
738 /* bAbort is TRUE (return is FALSE) if the user has aborted */
740 return !wxPrinterBase::sm_abortIt
;
744 // wxUSE_PRINTING_ARCHITECTURE