]> git.saurik.com Git - wxWidgets.git/blob - src/msw/printwin.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[wxWidgets.git] / src / msw / printwin.cpp
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 #if wxUSE_ENH_METAFILE
53 #include "wx/msw/enhmeta.h"
54 #endif
55 #include <stdlib.h>
56
57 // ---------------------------------------------------------------------------
58 // private functions
59 // ---------------------------------------------------------------------------
60
61 BOOL CALLBACK wxAbortProc(HDC hdc, int error);
62
63 // ---------------------------------------------------------------------------
64 // wxWin macros
65 // ---------------------------------------------------------------------------
66
67 IMPLEMENT_DYNAMIC_CLASS(wxWindowsPrinter, wxPrinterBase)
68 IMPLEMENT_CLASS(wxWindowsPrintPreview, wxPrintPreviewBase)
69
70 // ===========================================================================
71 // implementation
72 // ===========================================================================
73
74 // ---------------------------------------------------------------------------
75 // Printer
76 // ---------------------------------------------------------------------------
77
78 wxWindowsPrinter::wxWindowsPrinter(wxPrintDialogData *data)
79 : wxPrinterBase(data)
80 {
81 }
82
83 bool wxWindowsPrinter::Print(wxWindow *parent, wxPrintout *printout, bool prompt)
84 {
85 sm_abortIt = false;
86 sm_abortWindow = NULL;
87
88 if (!printout)
89 {
90 sm_lastError = wxPRINTER_ERROR;
91 return false;
92 }
93
94 if (m_printDialogData.GetMinPage() < 1)
95 m_printDialogData.SetMinPage(1);
96 if (m_printDialogData.GetMaxPage() < 1)
97 m_printDialogData.SetMaxPage(9999);
98
99 // Create a suitable device context
100 wxPrinterDC *dc wxDUMMY_INITIALIZE(NULL);
101 if (prompt)
102 {
103 dc = wxDynamicCast(PrintDialog(parent), wxPrinterDC);
104 if (!dc)
105 return false;
106 }
107 else
108 {
109 dc = new wxPrinterDC(m_printDialogData.GetPrintData());
110 }
111
112 // May have pressed cancel.
113 if (!dc || !dc->IsOk())
114 {
115 if (dc) delete dc;
116 return false;
117 }
118
119 wxPrinterDCImpl *impl = (wxPrinterDCImpl*) dc->GetImpl();
120
121 HDC hdc = ::GetDC(NULL);
122 int logPPIScreenX = ::GetDeviceCaps(hdc, LOGPIXELSX);
123 int logPPIScreenY = ::GetDeviceCaps(hdc, LOGPIXELSY);
124 ::ReleaseDC(NULL, hdc);
125
126 int logPPIPrinterX = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSX);
127 int logPPIPrinterY = ::GetDeviceCaps((HDC) impl->GetHDC(), LOGPIXELSY);
128 if (logPPIPrinterX == 0 || logPPIPrinterY == 0)
129 {
130 delete dc;
131 sm_lastError = wxPRINTER_ERROR;
132 return false;
133 }
134
135 printout->SetPPIScreen(logPPIScreenX, logPPIScreenY);
136 printout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY);
137
138 // Set printout parameters
139 printout->SetDC(dc);
140
141 int w, h;
142 dc->GetSize(&w, &h);
143 printout->SetPageSizePixels((int)w, (int)h);
144 printout->SetPaperRectPixels(dc->GetPaperRect());
145
146 dc->GetSizeMM(&w, &h);
147 printout->SetPageSizeMM((int)w, (int)h);
148
149 // Create an abort window
150 wxBusyCursor busyCursor;
151
152 printout->OnPreparePrinting();
153
154 // Get some parameters from the printout, if defined
155 int fromPage, toPage;
156 int minPage, maxPage;
157 printout->GetPageInfo(&minPage, &maxPage, &fromPage, &toPage);
158
159 if (maxPage == 0)
160 {
161 sm_lastError = wxPRINTER_ERROR;
162 return false;
163 }
164
165 // Only set min and max, because from and to have been
166 // set by the user
167 m_printDialogData.SetMinPage(minPage);
168 m_printDialogData.SetMaxPage(maxPage);
169
170 wxPrintAbortDialog *win = CreateAbortWindow(parent, printout);
171 wxYield();
172
173 ::SetAbortProc(GetHdcOf(*impl), wxAbortProc);
174
175 if (!win)
176 {
177 wxLogDebug(wxT("Could not create an abort dialog."));
178 sm_lastError = wxPRINTER_ERROR;
179
180 delete dc;
181 return false;
182 }
183 sm_abortWindow = win;
184 sm_abortWindow->Show();
185 wxSafeYield();
186
187 printout->OnBeginPrinting();
188
189 sm_lastError = wxPRINTER_NO_ERROR;
190
191 int minPageNum = minPage, maxPageNum = maxPage;
192
193 if ( !m_printDialogData.GetAllPages() )
194 {
195 minPageNum = m_printDialogData.GetFromPage();
196 maxPageNum = m_printDialogData.GetToPage();
197 }
198
199 const int maxCopyCount = m_printDialogData.GetNoCopies();
200 for ( int copyCount = 1; copyCount <= maxCopyCount; copyCount++ )
201 {
202 if ( !printout->OnBeginDocument(minPageNum, maxPageNum) )
203 {
204 wxLogError(_("Could not start printing."));
205 sm_lastError = wxPRINTER_ERROR;
206 break;
207 }
208 if (sm_abortIt)
209 {
210 sm_lastError = wxPRINTER_CANCELLED;
211 break;
212 }
213
214 int pn;
215
216 for ( pn = minPageNum;
217 pn <= maxPageNum && printout->HasPage(pn);
218 pn++ )
219 {
220 win->SetProgress(pn - minPageNum + 1,
221 maxPageNum - minPageNum + 1,
222 copyCount, maxCopyCount);
223
224 if ( sm_abortIt )
225 {
226 sm_lastError = wxPRINTER_CANCELLED;
227 break;
228 }
229
230 dc->StartPage();
231 bool cont = printout->OnPrintPage(pn);
232 dc->EndPage();
233
234 if ( !cont )
235 {
236 sm_lastError = wxPRINTER_CANCELLED;
237 break;
238 }
239 }
240
241 printout->OnEndDocument();
242 }
243
244 printout->OnEndPrinting();
245
246 if (sm_abortWindow)
247 {
248 sm_abortWindow->Show(false);
249 wxDELETE(sm_abortWindow);
250 }
251
252 delete dc;
253
254 return sm_lastError == wxPRINTER_NO_ERROR;
255 }
256
257 wxDC *wxWindowsPrinter::PrintDialog(wxWindow *parent)
258 {
259 wxDC *dc = NULL;
260
261 wxWindowsPrintDialog dialog(parent, & m_printDialogData);
262 int ret = dialog.ShowModal();
263
264 if (ret == wxID_OK)
265 {
266 dc = dialog.GetPrintDC();
267 m_printDialogData = dialog.GetPrintDialogData();
268 if (dc == NULL)
269 sm_lastError = wxPRINTER_ERROR;
270 else
271 sm_lastError = wxPRINTER_NO_ERROR;
272 }
273 else
274 sm_lastError = wxPRINTER_CANCELLED;
275
276 return dc;
277 }
278
279 bool wxWindowsPrinter::Setup(wxWindow *WXUNUSED(parent))
280 {
281 #if 0
282 // We no longer expose that dialog
283 wxPrintDialog dialog(parent, & m_printDialogData);
284 dialog.GetPrintDialogData().SetSetupDialog(true);
285
286 int ret = dialog.ShowModal();
287
288 if (ret == wxID_OK)
289 {
290 m_printDialogData = dialog.GetPrintDialogData();
291 }
292
293 return (ret == wxID_OK);
294 #else
295 return false;
296 #endif
297 }
298
299 /*
300 * Print preview
301 */
302
303 wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout,
304 wxPrintout *printoutForPrinting,
305 wxPrintDialogData *data)
306 : wxPrintPreviewBase(printout, printoutForPrinting, data)
307 {
308 DetermineScaling();
309 }
310
311 wxWindowsPrintPreview::wxWindowsPrintPreview(wxPrintout *printout,
312 wxPrintout *printoutForPrinting,
313 wxPrintData *data)
314 : wxPrintPreviewBase(printout, printoutForPrinting, data)
315 {
316 DetermineScaling();
317 }
318
319 wxWindowsPrintPreview::~wxWindowsPrintPreview()
320 {
321 }
322
323 bool wxWindowsPrintPreview::Print(bool interactive)
324 {
325 if (!m_printPrintout)
326 return false;
327 wxWindowsPrinter printer(&m_printDialogData);
328 return printer.Print(m_previewFrame, m_printPrintout, interactive);
329 }
330
331 void wxWindowsPrintPreview::DetermineScaling()
332 {
333 ScreenHDC dc;
334 int logPPIScreenX = ::GetDeviceCaps(dc, LOGPIXELSX);
335 int logPPIScreenY = ::GetDeviceCaps(dc, LOGPIXELSY);
336 m_previewPrintout->SetPPIScreen(logPPIScreenX, logPPIScreenY);
337
338 // Get a device context for the currently selected printer
339 wxPrinterDC printerDC(m_printDialogData.GetPrintData());
340
341 int printerWidthMM;
342 int printerHeightMM;
343 int printerXRes;
344 int printerYRes;
345 int logPPIPrinterX;
346 int logPPIPrinterY;
347
348 wxRect paperRect;
349
350 if ( printerDC.IsOk() )
351 {
352 wxPrinterDCImpl *impl = (wxPrinterDCImpl*) printerDC.GetImpl();
353 HDC dc = GetHdcOf(*impl);
354 printerWidthMM = ::GetDeviceCaps(dc, HORZSIZE);
355 printerHeightMM = ::GetDeviceCaps(dc, VERTSIZE);
356 printerXRes = ::GetDeviceCaps(dc, HORZRES);
357 printerYRes = ::GetDeviceCaps(dc, VERTRES);
358 logPPIPrinterX = ::GetDeviceCaps(dc, LOGPIXELSX);
359 logPPIPrinterY = ::GetDeviceCaps(dc, LOGPIXELSY);
360
361 paperRect = printerDC.GetPaperRect();
362
363 if ( logPPIPrinterX == 0 ||
364 logPPIPrinterY == 0 ||
365 printerWidthMM == 0 ||
366 printerHeightMM == 0 )
367 {
368 m_isOk = false;
369 }
370 }
371 else
372 {
373 // use some defaults
374 printerWidthMM = 150;
375 printerHeightMM = 250;
376 printerXRes = 1500;
377 printerYRes = 2500;
378 logPPIPrinterX = 600;
379 logPPIPrinterY = 600;
380
381 paperRect = wxRect(0, 0, printerXRes, printerYRes);
382 m_isOk = false;
383 }
384 m_pageWidth = printerXRes;
385 m_pageHeight = printerYRes;
386 m_previewPrintout->SetPageSizePixels(printerXRes, printerYRes);
387 m_previewPrintout->SetPageSizeMM(printerWidthMM, printerHeightMM);
388 m_previewPrintout->SetPaperRectPixels(paperRect);
389 m_previewPrintout->SetPPIPrinter(logPPIPrinterX, logPPIPrinterY);
390
391 // At 100%, the page should look about page-size on the screen.
392 m_previewScaleX = float(logPPIScreenX) / logPPIPrinterX;
393 m_previewScaleY = float(logPPIScreenY) / logPPIPrinterY;
394 }
395
396 #if wxUSE_ENH_METAFILE
397 bool wxWindowsPrintPreview::RenderPageIntoBitmap(wxBitmap& bmp, int pageNum)
398 {
399 // The preview, as implemented in wxPrintPreviewBase (and as used prior to
400 // wx3) is inexact: it uses screen DC, which has much lower resolution and
401 // has other properties different from printer DC, so the preview is not
402 // quite right.
403 //
404 // To make matters worse, if the application depends heavily on
405 // GetTextExtent() or does text layout itself, the output in preview and on
406 // paper can be very different. In particular, wxHtmlEasyPrinting is
407 // affected and the preview can be easily off by several pages.
408 //
409 // To fix this, we render the preview into high-resolution enhanced
410 // metafile with properties identical to the printer DC. This guarantees
411 // metrics correctness while still being fast.
412
413
414 // print the preview into a metafile:
415 wxPrinterDC printerDC(m_printDialogData.GetPrintData());
416 wxEnhMetaFileDC metaDC(printerDC,
417 wxEmptyString,
418 printerDC.GetSize().x, printerDC.GetSize().y);
419
420 if ( !RenderPageIntoDC(metaDC, pageNum) )
421 return false;
422
423 wxEnhMetaFile *metafile = metaDC.Close();
424 if ( !metafile )
425 return false;
426
427 // now render the metafile:
428 wxMemoryDC bmpDC;
429 bmpDC.SelectObject(bmp);
430 bmpDC.Clear();
431
432 wxRect outRect(0, 0, bmp.GetWidth(), bmp.GetHeight());
433 metafile->Play(&bmpDC, &outRect);
434
435
436 delete metafile;
437
438 // TODO: we should keep the metafile and reuse it when changing zoom level
439
440 return true;
441 }
442 #endif // wxUSE_ENH_METAFILE
443
444 BOOL CALLBACK wxAbortProc(HDC WXUNUSED(hdc), int WXUNUSED(error))
445 {
446 MSG msg;
447
448 if (!wxPrinterBase::sm_abortWindow) /* If the abort dialog isn't up yet */
449 return(TRUE);
450
451 /* Process messages intended for the abort dialog box */
452
453 while (!wxPrinterBase::sm_abortIt && ::PeekMessage(&msg, 0, 0, 0, TRUE))
454 if (!IsDialogMessage((HWND) wxPrinterBase::sm_abortWindow->GetHWND(), &msg)) {
455 TranslateMessage(&msg);
456 DispatchMessage(&msg);
457 }
458
459 /* bAbort is TRUE (return is FALSE) if the user has aborted */
460
461 return !wxPrinterBase::sm_abortIt;
462 }
463
464 #endif
465 // wxUSE_PRINTING_ARCHITECTURE