X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/373c713ad4eed29128b0619b508da4a29698d145..1e5b533ec0a5b459fe2ac58abd6f21e5b1e51a92:/src/msw/printwin.cpp diff --git a/src/msw/printwin.cpp b/src/msw/printwin.cpp index 281521c391..62abb589f6 100644 --- a/src/msw/printwin.cpp +++ b/src/msw/printwin.cpp @@ -49,18 +49,16 @@ #include "wx/msw/printdlg.h" #include "wx/msw/private.h" #include "wx/msw/dcprint.h" - -#include - -#ifndef __WIN32__ - #include +#if wxUSE_ENH_METAFILE + #include "wx/msw/enhmeta.h" #endif +#include // --------------------------------------------------------------------------- // private functions // --------------------------------------------------------------------------- -LONG APIENTRY _EXPORT wxAbortProc(HDC hPr, int Code); +BOOL CALLBACK wxAbortProc(HDC hdc, int error); // --------------------------------------------------------------------------- // wxWin macros @@ -80,16 +78,6 @@ LONG APIENTRY _EXPORT wxAbortProc(HDC hPr, int Code); wxWindowsPrinter::wxWindowsPrinter(wxPrintDialogData *data) : wxPrinterBase(data) { - m_lpAbortProc = (WXFARPROC)wxAbortProc; -} - -wxWindowsPrinter::~wxWindowsPrinter() -{ - // avoids warning about statement with no effect (FreeProcInstance - // doesn't do anything under Win32) -#if !defined(__WIN32__) && !defined(__NT__) - FreeProcInstance((FARPROC) m_lpAbortProc); -#endif } bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt) @@ -103,8 +91,6 @@ bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt return false; } - printout->SetIsPreview(false); - if (m_printDialogData.GetMinPage() < 1) m_printDialogData.SetMinPage(1); if (m_printDialogData.GetMaxPage() < 1) @@ -181,27 +167,10 @@ bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt m_printDialogData.SetMinPage(minPage); m_printDialogData.SetMaxPage(maxPage); - wxWindow *win = CreateAbortWindow(parent, printout); + wxPrintAbortDialog *win = CreateAbortWindow(parent, printout); wxYield(); -#if defined(__WATCOMC__) || defined(__BORLANDC__) || defined(__GNUWIN32__) || !defined(__WIN32__) -#ifdef STRICT - ::SetAbortProc((HDC) impl->GetHDC(), (ABORTPROC) m_lpAbortProc); -#else - ::SetAbortProc((HDC) impl->GetHDC(), (FARPROC) m_lpAbortProc); -#endif -#else - ::SetAbortProc((HDC) impl->GetHDC(), (int (_stdcall *) - // cast it to right type only if required - // FIXME it's really cdecl and we're casting it to stdcall - either there is - // something I don't understand or it will crash at first usage -#ifdef STRICT - (HDC, int) -#else - () -#endif - )m_lpAbortProc); -#endif + ::SetAbortProc(GetHdcOf(*impl), wxAbortProc); if (!win) { @@ -227,10 +196,15 @@ bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt maxPageNum = m_printDialogData.GetToPage(); } - int copyCount; - for ( copyCount = 1; - copyCount <= m_printDialogData.GetNoCopies(); - copyCount++ ) + // The dc we get from the PrintDialog will do multiple copies without help + // if the device supports it. Loop only if we have created a dc from our + // own m_printDialogData or the device does not support multiple copies. + // m_printDialogData.GetPrintData().GetNoCopies() is set from device + // devMode in printdlg.cpp/wxWindowsPrintDialog::ConvertFromNative() + const int maxCopyCount = !prompt || + !m_printDialogData.GetPrintData().GetNoCopies() + ? m_printDialogData.GetNoCopies() : 1; + for ( int copyCount = 1; copyCount <= maxCopyCount; copyCount++ ) { if ( !printout->OnBeginDocument(minPageNum, maxPageNum) ) { @@ -250,6 +224,10 @@ bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt pn <= maxPageNum && printout->HasPage(pn); pn++ ) { + win->SetProgress(pn - minPageNum + 1, + maxPageNum - minPageNum + 1, + copyCount, maxCopyCount); + if ( sm_abortIt ) { sm_lastError = wxPRINTER_CANCELLED; @@ -275,8 +253,7 @@ bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt if (sm_abortWindow) { sm_abortWindow->Show(false); - delete sm_abortWindow; - sm_abortWindow = NULL; + wxDELETE(sm_abortWindow); } delete dc; @@ -286,7 +263,7 @@ bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt wxDC *wxWindowsPrinter::PrintDialog(wxWindow *parent) { - wxDC *dc = (wxPrinterDC*) NULL; + wxDC *dc = NULL; wxWindowsPrintDialog dialog(parent, & m_printDialogData); int ret = dialog.ShowModal(); @@ -335,9 +312,6 @@ wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, wxPrintDialogData *data) : wxPrintPreviewBase(printout, printoutForPrinting, data) { -#if wxUSE_HIGH_QUALITY_PREVIEW - m_hqPreviewFailed = false; -#endif DetermineScaling(); } @@ -346,9 +320,6 @@ wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, wxPrintData *data) : wxPrintPreviewBase(printout, printoutForPrinting, data) { -#if wxUSE_HIGH_QUALITY_PREVIEW - m_hqPreviewFailed = false; -#endif DetermineScaling(); } @@ -429,306 +400,55 @@ void wxWindowsPrintPreview::DetermineScaling() m_previewScaleY = float(logPPIScreenY) / logPPIPrinterY; } - -#if wxUSE_HIGH_QUALITY_PREVIEW - -// The preview, as implemented in wxPrintPreviewBase (and as used prior to wx3) -// is inexact: it uses screen DC, which has much lower resolution and has -// other properties different from printer DC, so the preview is not quite -// right. -// -// To make matters worse, if the application depends heavily on GetTextExtent() -// or does text layout itself, the output in preview and on paper can be very -// different. In particular, wxHtmlEasyPrinting is affected and the preview -// can be easily off by several pages. -// -// To fix this, we attempt to render the preview into high-resolution bitmap -// using DC with same resolution etc. as the printer DC. This takes lot of -// memory, so the code is more complicated than it could be, but the results -// are much better. -// -// Finally, this code is specific to wxMSW, because it doesn't make sense to -// bother with it on other platforms. Both OSX and modern GNOME/GTK+ -// environments have builtin accurate preview (that applications should use -// instead) and the differences between screen and printer DC in wxGTK are so -// large than this trick doesn't help at all. - -namespace -{ - -// If there's not enough memory, we need to render the preview in parts. -// Unfortunately we cannot simply use wxMemoryDC, because it reports its size -// as bitmap's size, and we need to use smaller bitmap while still reporting -// original ("correct") DC size, because printing code frequently uses -// GetSize() to determine scaling factor. This DC class handles this. - -class PageFragmentDCImpl : public wxMemoryDCImpl -{ -public: - PageFragmentDCImpl(wxMemoryDC *owner, wxDC *printer, - const wxPoint& offset, - const wxSize& fullSize) - : wxMemoryDCImpl(owner, printer), - m_offset(offset), - m_fullSize(fullSize) - { - SetDeviceOrigin(0, 0); - } - - virtual void SetDeviceOrigin(wxCoord x, wxCoord y) - { - wxMemoryDCImpl::SetDeviceOrigin(x - m_offset.x, y - m_offset.y); - } - - virtual void DoGetDeviceOrigin(wxCoord *x, wxCoord *y) const - { - wxMemoryDCImpl::DoGetDeviceOrigin(x, y); - if ( x ) *x += m_offset.x; - if ( x ) *y += m_offset.y; - } - - virtual void DoGetSize(int *width, int *height) const - { - if ( width ) - *width = m_fullSize.x; - if ( height ) - *height = m_fullSize.y; - } - -private: - wxPoint m_offset; - wxSize m_fullSize; -}; - -class PageFragmentDC : public wxDC -{ -public: - PageFragmentDC(wxDC* printer, wxBitmap& bmp, - const wxPoint& offset, - const wxSize& fullSize) - : wxDC(new PageFragmentDCImpl((wxMemoryDC*)this, printer, offset, fullSize)) - { - wx_static_cast(PageFragmentDCImpl*, m_pimpl)->DoSelect(bmp); - } -}; - -// estimate how big chunks we can render, given available RAM -long ComputeFragmentSize(long printerDepth, - long width, - long height) +#if wxUSE_ENH_METAFILE +bool wxWindowsPrintPreview::RenderPageIntoBitmap(wxBitmap& bmp, int pageNum) { - // Compute the amount of memory needed to generate the preview. - // Memory requirements of RenderPageFragment() are as follows: + // The preview, as implemented in wxPrintPreviewBase (and as used prior to + // wx3) is inexact: it uses screen DC, which has much lower resolution and + // has other properties different from printer DC, so the preview is not + // quite right. // - // (memory DC - always) - // width * height * printerDepth/8 - // (wxImage + wxDIB instance) - // width * height * (3 + 4) - // (this could be reduced to *3 if using wxGraphicsContext) + // To make matters worse, if the application depends heavily on + // GetTextExtent() or does text layout itself, the output in preview and on + // paper can be very different. In particular, wxHtmlEasyPrinting is + // affected and the preview can be easily off by several pages. // - // So, given amount of memory M, we can render at most - // - // height = M / (width * (printerDepth/8 + F)) - // - // where F is 3 or 7 depending on whether wxGraphicsContext is used or not - - wxMemorySize memAvail = wxGetFreeMemory(); - if ( memAvail == -1 ) - { - // we don't know; 10meg shouldn't be a problem hopefully - memAvail = 10000000; - } - else - { - // limit ourselves to half of available RAM to have a margin for other - // apps, for our rendering code, and for miscalculations - memAvail /= 2; - } - - const float perPixel = float(printerDepth)/8 + (3 + 4); - - const long perLine = long(width * perPixel); - const long maxstep = (memAvail / perLine).GetValue(); - const long step = wxMin(height, maxstep); - - wxLogTrace("printing", - "using %liMB of RAM (%li lines) for preview, %li %lipx fragments", - long((memAvail >> 20).GetValue()), - maxstep, - (height+step-1) / step, - step); - - return step; -} + // To fix this, we render the preview into high-resolution enhanced + // metafile with properties identical to the printer DC. This guarantees + // metrics correctness while still being fast. -} // anonymous namespace + // print the preview into a metafile: + wxPrinterDC printerDC(m_printDialogData.GetPrintData()); + wxEnhMetaFileDC metaDC(printerDC, + wxEmptyString, + printerDC.GetSize().x, printerDC.GetSize().y); -bool wxWindowsPrintPreview::RenderPageFragment(float scaleX, float scaleY, - int *nextFinalLine, - wxPrinterDC& printer, - wxMemoryDC& finalDC, - const wxRect& rect, - int pageNum) -{ - // compute 'rect' equivalent in the small final bitmap: - const wxRect smallRect(wxPoint(0, *nextFinalLine), - wxPoint(int(rect.GetRight() * scaleX), - int(rect.GetBottom() * scaleY))); - wxLogTrace("printing", - "rendering fragment of page %i: [%i,%i,%i,%i] scaled down to [%i,%i,%i,%i]", - pageNum, - rect.x, rect.y, rect.GetRight(), rect.GetBottom(), - smallRect.x, smallRect.y, smallRect.GetRight(), smallRect.GetBottom() - ); - - // create DC and bitmap compatible with printer DC: - wxBitmap large(rect.width, rect.height, printer); - if ( !large.IsOk() ) - return false; - - // render part of the page into it: - { - PageFragmentDC memoryDC(&printer, large, - rect.GetPosition(), - wxSize(m_pageWidth, m_pageHeight)); - if ( !memoryDC.IsOk() ) - return false; - - memoryDC.Clear(); - - if ( !RenderPageIntoDC(memoryDC, pageNum) ) - return false; - } // release bitmap from memoryDC - - // now scale the rendered part down and blit it into final output: - - wxImage img; - { - wxDIB dib(large); - if ( !dib.IsOk() ) - return false; - large = wxNullBitmap; // free memory a.s.a.p. - img = dib.ConvertToImage(); - } // free the DIB now that it's no longer needed, too - - if ( !img.IsOk() ) - return false; - - img.Rescale(smallRect.width, smallRect.height, wxIMAGE_QUALITY_HIGH); - if ( !img.IsOk() ) - return false; - - wxBitmap bmp(img); - if ( !bmp.IsOk() ) + if ( !RenderPageIntoDC(metaDC, pageNum) ) return false; - img = wxNullImage; - finalDC.DrawBitmap(bmp, smallRect.x, smallRect.y); - if ( bmp.IsOk() ) - { - *nextFinalLine += smallRect.height; - return true; - } - - return false; -} - -bool wxWindowsPrintPreview::RenderPageIntoBitmapHQ(wxBitmap& bmp, int pageNum) -{ - wxLogTrace("printing", "rendering HQ preview of page %i", pageNum); - - wxPrinterDC printerDC(m_printDialogData.GetPrintData()); - if ( !printerDC.IsOk() ) + wxEnhMetaFile *metafile = metaDC.Close(); + if ( !metafile ) return false; - // compute scale factor - const float scaleX = float(bmp.GetWidth()) / float(m_pageWidth); - const float scaleY = float(bmp.GetHeight()) / float(m_pageHeight); - + // now render the metafile: wxMemoryDC bmpDC; bmpDC.SelectObject(bmp); bmpDC.Clear(); - const int initialStep = ComputeFragmentSize(printerDC.GetDepth(), - m_pageWidth, m_pageHeight); - - wxRect todo(0, 0, m_pageWidth, initialStep); // rect to render - int nextFinalLine = 0; // first not-yet-rendered output line - - while ( todo.y < m_pageHeight ) - { - todo.SetBottom(wxMin(todo.GetBottom(), m_pageHeight - 1)); - - if ( !RenderPageFragment(scaleX, scaleY, - &nextFinalLine, - printerDC, - bmpDC, - todo, - pageNum) ) - { - if ( todo.height < 20 ) - { - // something is very wrong if we can't render even at this - // slow space, let's bail out and fall back to low quality - // preview - wxLogTrace("printing", - "it seems that HQ preview doesn't work at all"); - return false; - } - - // it's possible our memory calculation was off, or conditions - // changed, or there's not enough _bitmap_ resources; try if using - // smaller bitmap would help: - todo.height /= 2; + wxRect outRect(0, 0, bmp.GetWidth(), bmp.GetHeight()); + metafile->Play(&bmpDC, &outRect); - wxLogTrace("printing", - "preview of fragment failed, reducing height to %ipx", - todo.height); - continue; // retry at the same position again - } + delete metafile; - // move to the next segment - todo.Offset(0, todo.height); - } + // TODO: we should keep the metafile and reuse it when changing zoom level return true; } +#endif // wxUSE_ENH_METAFILE -bool wxWindowsPrintPreview::RenderPageIntoBitmap(wxBitmap& bmp, int pageNum) -{ - // try high quality rendering first: - if ( !m_hqPreviewFailed ) - { - if ( RenderPageIntoBitmapHQ(bmp, pageNum) ) - { - return true; - } - else - { - wxLogTrace("printing", - "high-quality preview failed, falling back to normal"); - m_hqPreviewFailed = true; // don't bother re-trying - } - } - - // if it fails, use the generic method that is less resource intensive, - // but inexact - return wxPrintPreviewBase::RenderPageIntoBitmap(bmp, pageNum); -} - -#endif // wxUSE_HIGH_QUALITY_PREVIEW - -/**************************************************************************** - - FUNCTION: wxAbortProc() - - PURPOSE: Processes messages for the Abort Dialog box - -****************************************************************************/ - -LONG APIENTRY _EXPORT wxAbortProc(HDC WXUNUSED(hPr), int WXUNUSED(Code)) +BOOL CALLBACK wxAbortProc(HDC WXUNUSED(hdc), int WXUNUSED(error)) { MSG msg;