X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/25a3fca2e516180ee80bdd9ef5321fe8b80673ea..79d81c45ca3e1b6c6d0c8712986e0b3ab42ad28f:/src/msw/printwin.cpp diff --git a/src/msw/printwin.cpp b/src/msw/printwin.cpp index 38e384468f..587a4749fc 100644 --- a/src/msw/printwin.cpp +++ b/src/msw/printwin.cpp @@ -49,6 +49,7 @@ #include "wx/msw/printdlg.h" #include "wx/msw/private.h" #include "wx/msw/dcprint.h" +#include "wx/msw/enhmeta.h" #include @@ -286,7 +287,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 +336,6 @@ wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, wxPrintDialogData *data) : wxPrintPreviewBase(printout, printoutForPrinting, data) { -#if wxUSE_HIGH_QUALITY_PREVIEW - m_hqPreviewFailed = false; -#endif DetermineScaling(); } @@ -346,9 +344,6 @@ wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout, wxPrintData *data) : wxPrintPreviewBase(printout, printoutForPrinting, data) { -#if wxUSE_HIGH_QUALITY_PREVIEW - m_hqPreviewFailed = false; -#endif DetermineScaling(); } @@ -429,288 +424,52 @@ 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 wxRect& rect) - : wxMemoryDCImpl(owner, printer), - m_rect(rect) - { - SetDeviceOrigin(0, 0); - } - - virtual void SetDeviceOrigin(wxCoord x, wxCoord y) - { - wxMemoryDCImpl::SetDeviceOrigin(x - m_rect.x, y - m_rect.y); - } - - virtual void DoGetDeviceOrigin(wxCoord *x, wxCoord *y) const - { - wxMemoryDCImpl::DoGetDeviceOrigin(x, y); - if ( x ) *x += m_rect.x; - if ( x ) *y += m_rect.y; - } - - virtual void DoGetSize(int *width, int *height) const - { - if ( width ) - *width = m_rect.width; - if ( height ) - *height = m_rect.height; - } - -private: - wxRect m_rect; -}; - -class PageFragmentDC : public wxDC -{ -public: - PageFragmentDC(wxDC* printer, wxBitmap& bmp, const wxRect& rect) - : wxDC(new PageFragmentDCImpl((wxMemoryDC*)this, printer, rect)) - { - 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) +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; - } + // 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. - 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; -} - -} // anonymous namespace - - -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); - 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; + // print the preview into a metafile: + wxPrinterDC printerDC(m_printDialogData.GetPrintData()); + wxEnhMetaFileDC metaDC(printerDC, + wxEmptyString, + printerDC.GetSize().x, printerDC.GetSize().y); - 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 outRect(0, 0, bmp.GetWidth(), bmp.GetHeight()); + metafile->Play(&bmpDC, &outRect); - 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; + delete metafile; - wxLogTrace("printing", - "preview of fragment failed, reducing height to %ipx", - todo.height); - - continue; // retry at the same position again - } - - // 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; } -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 /****************************************************************************