Move Start/EndPage() from NewPage/PageDone()
[chromium-blink-merge.git] / printing / printing_context_win.cc
blob32a44be1d14fad9c7fae77323d8c234ca37e4932
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "printing/printing_context_win.h"
7 #include <winspool.h>
9 #include "base/i18n/file_util_icu.h"
10 #include "base/i18n/time_formatting.h"
11 #include "base/message_loop.h"
12 #include "base/time.h"
13 #include "base/utf_string_conversions.h"
14 #include "printing/print_settings_initializer_win.h"
15 #include "printing/printed_document.h"
16 #include "skia/ext/platform_device_win.h"
18 using base::Time;
20 namespace printing {
22 class PrintingContextWin::CallbackHandler : public IPrintDialogCallback,
23 public IObjectWithSite {
24 public:
25 CallbackHandler(PrintingContextWin& owner, HWND owner_hwnd)
26 : owner_(owner),
27 owner_hwnd_(owner_hwnd),
28 services_(NULL) {
31 ~CallbackHandler() {
32 if (services_)
33 services_->Release();
36 IUnknown* ToIUnknown() {
37 return static_cast<IUnknown*>(static_cast<IPrintDialogCallback*>(this));
40 // IUnknown
41 virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) {
42 if (riid == IID_IUnknown) {
43 *object = ToIUnknown();
44 } else if (riid == IID_IPrintDialogCallback) {
45 *object = static_cast<IPrintDialogCallback*>(this);
46 } else if (riid == IID_IObjectWithSite) {
47 *object = static_cast<IObjectWithSite*>(this);
48 } else {
49 return E_NOINTERFACE;
51 return S_OK;
54 // No real ref counting.
55 virtual ULONG WINAPI AddRef() {
56 return 1;
58 virtual ULONG WINAPI Release() {
59 return 1;
62 // IPrintDialogCallback methods
63 virtual HRESULT WINAPI InitDone() {
64 return S_OK;
67 virtual HRESULT WINAPI SelectionChange() {
68 if (services_) {
69 // TODO(maruel): Get the devmode for the new printer with
70 // services_->GetCurrentDevMode(&devmode, &size), send that information
71 // back to our client and continue. The client needs to recalculate the
72 // number of rendered pages and send back this information here.
74 return S_OK;
77 virtual HRESULT WINAPI HandleMessage(HWND dialog,
78 UINT message,
79 WPARAM wparam,
80 LPARAM lparam,
81 LRESULT* result) {
82 // Cheap way to retrieve the window handle.
83 if (!owner_.dialog_box_) {
84 // The handle we receive is the one of the groupbox in the General tab. We
85 // need to get the grand-father to get the dialog box handle.
86 owner_.dialog_box_ = GetAncestor(dialog, GA_ROOT);
87 // Trick to enable the owner window. This can cause issues with navigation
88 // events so it may have to be disabled if we don't fix the side-effects.
89 EnableWindow(owner_hwnd_, TRUE);
91 return S_FALSE;
94 virtual HRESULT WINAPI SetSite(IUnknown* site) {
95 if (!site) {
96 DCHECK(services_);
97 services_->Release();
98 services_ = NULL;
99 // The dialog box is destroying, PrintJob::Worker don't need the handle
100 // anymore.
101 owner_.dialog_box_ = NULL;
102 } else {
103 DCHECK(services_ == NULL);
104 HRESULT hr = site->QueryInterface(IID_IPrintDialogServices,
105 reinterpret_cast<void**>(&services_));
106 DCHECK(SUCCEEDED(hr));
108 return S_OK;
111 virtual HRESULT WINAPI GetSite(REFIID riid, void** site) {
112 return E_NOTIMPL;
115 private:
116 PrintingContextWin& owner_;
117 HWND owner_hwnd_;
118 IPrintDialogServices* services_;
120 DISALLOW_COPY_AND_ASSIGN(CallbackHandler);
123 // static
124 PrintingContext* PrintingContext::Create(const std::string& app_locale) {
125 return static_cast<PrintingContext*>(new PrintingContextWin(app_locale));
128 PrintingContextWin::PrintingContextWin(const std::string& app_locale)
129 : PrintingContext(app_locale),
130 context_(NULL),
131 dialog_box_(NULL),
132 print_dialog_func_(&PrintDlgEx) {
135 PrintingContextWin::~PrintingContextWin() {
136 ReleaseContext();
139 void PrintingContextWin::AskUserForSettings(HWND view,
140 int max_pages,
141 bool has_selection,
142 PrintSettingsCallback* callback) {
143 DCHECK(!in_print_job_);
144 dialog_box_dismissed_ = false;
146 HWND window;
147 if (!view || !IsWindow(view)) {
148 // TODO(maruel): bug 1214347 Get the right browser window instead.
149 window = GetDesktopWindow();
150 } else {
151 window = GetAncestor(view, GA_ROOTOWNER);
153 DCHECK(window);
155 // Show the OS-dependent dialog box.
156 // If the user press
157 // - OK, the settings are reset and reinitialized with the new settings. OK is
158 // returned.
159 // - Apply then Cancel, the settings are reset and reinitialized with the new
160 // settings. CANCEL is returned.
161 // - Cancel, the settings are not changed, the previous setting, if it was
162 // initialized before, are kept. CANCEL is returned.
163 // On failure, the settings are reset and FAILED is returned.
164 PRINTDLGEX dialog_options = { sizeof(PRINTDLGEX) };
165 dialog_options.hwndOwner = window;
166 // Disable options we don't support currently.
167 // TODO(maruel): Reuse the previously loaded settings!
168 dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
169 PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
170 if (!has_selection)
171 dialog_options.Flags |= PD_NOSELECTION;
173 PRINTPAGERANGE ranges[32];
174 dialog_options.nStartPage = START_PAGE_GENERAL;
175 if (max_pages) {
176 // Default initialize to print all the pages.
177 memset(ranges, 0, sizeof(ranges));
178 ranges[0].nFromPage = 1;
179 ranges[0].nToPage = max_pages;
180 dialog_options.nPageRanges = 1;
181 dialog_options.nMaxPageRanges = arraysize(ranges);
182 dialog_options.nMinPage = 1;
183 dialog_options.nMaxPage = max_pages;
184 dialog_options.lpPageRanges = ranges;
185 } else {
186 // No need to bother, we don't know how many pages are available.
187 dialog_options.Flags |= PD_NOPAGENUMS;
190 if ((*print_dialog_func_)(&dialog_options) != S_OK) {
191 ResetSettings();
192 callback->Run(FAILED);
195 // TODO(maruel): Support PD_PRINTTOFILE.
196 callback->Run(ParseDialogResultEx(dialog_options));
199 PrintingContext::Result PrintingContextWin::UseDefaultSettings() {
200 DCHECK(!in_print_job_);
202 PRINTDLG dialog_options = { sizeof(PRINTDLG) };
203 dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
204 if (PrintDlg(&dialog_options) == 0) {
205 ResetSettings();
206 return FAILED;
208 return ParseDialogResult(dialog_options);
211 PrintingContext::Result PrintingContextWin::UpdatePrintSettings(
212 const PageRanges& ranges) {
213 DCHECK(!in_print_job_);
215 settings_.ranges = ranges;
217 NOTIMPLEMENTED();
219 return FAILED;
222 PrintingContext::Result PrintingContextWin::InitWithSettings(
223 const PrintSettings& settings) {
224 DCHECK(!in_print_job_);
226 settings_ = settings;
228 // TODO(maruel): settings_.ToDEVMODE()
229 HANDLE printer;
230 if (!OpenPrinter(const_cast<wchar_t*>(settings_.device_name().c_str()),
231 &printer,
232 NULL))
233 return FAILED;
235 Result status = OK;
237 if (!GetPrinterSettings(printer, settings_.device_name()))
238 status = FAILED;
240 // Close the printer after retrieving the context.
241 ClosePrinter(printer);
243 if (status != OK)
244 ResetSettings();
245 return status;
248 PrintingContext::Result PrintingContextWin::NewDocument(
249 const string16& document_name) {
250 DCHECK(!in_print_job_);
251 if (!context_)
252 return OnError();
254 // Set the flag used by the AbortPrintJob dialog procedure.
255 abort_printing_ = false;
257 in_print_job_ = true;
259 // Register the application's AbortProc function with GDI.
260 if (SP_ERROR == SetAbortProc(context_, &AbortProc))
261 return OnError();
263 DOCINFO di = { sizeof(DOCINFO) };
264 const std::wstring& document_name_wide = UTF16ToWide(document_name);
265 di.lpszDocName = document_name_wide.c_str();
267 // Is there a debug dump directory specified? If so, force to print to a file.
268 FilePath debug_dump_path = PrintedDocument::debug_dump_path();
269 if (!debug_dump_path.empty()) {
270 // Create a filename.
271 std::wstring filename;
272 Time now(Time::Now());
273 filename = base::TimeFormatShortDateNumeric(now);
274 filename += L"_";
275 filename += base::TimeFormatTimeOfDay(now);
276 filename += L"_";
277 filename += UTF16ToWide(document_name);
278 filename += L"_";
279 filename += L"buffer.prn";
280 file_util::ReplaceIllegalCharactersInPath(&filename, '_');
281 debug_dump_path.Append(filename);
282 di.lpszOutput = debug_dump_path.value().c_str();
285 // No message loop running in unit tests.
286 DCHECK(!MessageLoop::current() ? true :
287 !MessageLoop::current()->NestableTasksAllowed());
289 // Begin a print job by calling the StartDoc function.
290 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
291 // IPC. Make sure recursive task processing is disabled.
292 if (StartDoc(context_, &di) <= 0)
293 return OnError();
295 return OK;
298 PrintingContext::Result PrintingContextWin::NewPage() {
299 if (abort_printing_)
300 return CANCEL;
301 DCHECK(context_);
302 DCHECK(in_print_job_);
304 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
305 // ::StartPage().
307 return OK;
310 PrintingContext::Result PrintingContextWin::PageDone() {
311 if (abort_printing_)
312 return CANCEL;
313 DCHECK(in_print_job_);
315 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
316 // ::EndPage().
318 return OK;
321 PrintingContext::Result PrintingContextWin::DocumentDone() {
322 if (abort_printing_)
323 return CANCEL;
324 DCHECK(in_print_job_);
325 DCHECK(context_);
327 // Inform the driver that document has ended.
328 if (EndDoc(context_) <= 0)
329 return OnError();
331 ResetSettings();
332 return OK;
335 void PrintingContextWin::Cancel() {
336 abort_printing_ = true;
337 in_print_job_ = false;
338 if (context_)
339 CancelDC(context_);
340 if (dialog_box_) {
341 DestroyWindow(dialog_box_);
342 dialog_box_dismissed_ = true;
346 void PrintingContextWin::ReleaseContext() {
347 if (context_) {
348 DeleteDC(context_);
349 context_ = NULL;
353 gfx::NativeDrawingContext PrintingContextWin::context() const {
354 return context_;
357 // static
358 BOOL PrintingContextWin::AbortProc(HDC hdc, int nCode) {
359 if (nCode) {
360 // TODO(maruel): Need a way to find the right instance to set. Should
361 // leverage PrintJobManager here?
362 // abort_printing_ = true;
364 return true;
367 bool PrintingContextWin::InitializeSettings(const DEVMODE& dev_mode,
368 const std::wstring& new_device_name,
369 const PRINTPAGERANGE* ranges,
370 int number_ranges,
371 bool selection_only) {
372 skia::PlatformDevice::InitializeDC(context_);
373 DCHECK(GetDeviceCaps(context_, CLIPCAPS));
374 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB);
375 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64);
376 // Some printers don't advertise these.
377 // DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_SCALING);
378 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_CONST_ALPHA);
379 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
381 // StretchDIBits() support is needed for printing.
382 if (!(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB) ||
383 !(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64)) {
384 NOTREACHED();
385 ResetSettings();
386 return false;
389 DCHECK(!in_print_job_);
390 DCHECK(context_);
391 PageRanges ranges_vector;
392 if (!selection_only) {
393 // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
394 ranges_vector.reserve(number_ranges);
395 for (int i = 0; i < number_ranges; ++i) {
396 PageRange range;
397 // Transfer from 1-based to 0-based.
398 range.from = ranges[i].nFromPage - 1;
399 range.to = ranges[i].nToPage - 1;
400 ranges_vector.push_back(range);
404 PrintSettingsInitializerWin::InitPrintSettings(context_,
405 dev_mode,
406 ranges_vector,
407 new_device_name,
408 selection_only,
409 &settings_);
411 return true;
414 bool PrintingContextWin::GetPrinterSettings(HANDLE printer,
415 const std::wstring& device_name) {
416 DCHECK(!in_print_job_);
417 scoped_array<uint8> buffer;
419 // A PRINTER_INFO_9 structure specifying the per-user default printer
420 // settings.
421 GetPrinterHelper(printer, 9, &buffer);
422 if (buffer.get()) {
423 PRINTER_INFO_9* info_9 = reinterpret_cast<PRINTER_INFO_9*>(buffer.get());
424 if (info_9->pDevMode != NULL) {
425 if (!AllocateContext(device_name, info_9->pDevMode, &context_)) {
426 ResetSettings();
427 return false;
429 return InitializeSettings(*info_9->pDevMode, device_name, NULL, 0, false);
431 buffer.reset();
434 // A PRINTER_INFO_8 structure specifying the global default printer settings.
435 GetPrinterHelper(printer, 8, &buffer);
436 if (buffer.get()) {
437 PRINTER_INFO_8* info_8 = reinterpret_cast<PRINTER_INFO_8*>(buffer.get());
438 if (info_8->pDevMode != NULL) {
439 if (!AllocateContext(device_name, info_8->pDevMode, &context_)) {
440 ResetSettings();
441 return false;
443 return InitializeSettings(*info_8->pDevMode, device_name, NULL, 0, false);
445 buffer.reset();
448 // A PRINTER_INFO_2 structure specifying the driver's default printer
449 // settings.
450 GetPrinterHelper(printer, 2, &buffer);
451 if (buffer.get()) {
452 PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(buffer.get());
453 if (info_2->pDevMode != NULL) {
454 if (!AllocateContext(device_name, info_2->pDevMode, &context_)) {
455 ResetSettings();
456 return false;
458 return InitializeSettings(*info_2->pDevMode, device_name, NULL, 0, false);
460 buffer.reset();
462 // Failed to retrieve the printer settings.
463 ResetSettings();
464 return false;
467 // static
468 bool PrintingContextWin::AllocateContext(const std::wstring& printer_name,
469 const DEVMODE* dev_mode,
470 gfx::NativeDrawingContext* context) {
471 *context = CreateDC(L"WINSPOOL", printer_name.c_str(), NULL, dev_mode);
472 DCHECK(*context);
473 return *context != NULL;
476 PrintingContext::Result PrintingContextWin::ParseDialogResultEx(
477 const PRINTDLGEX& dialog_options) {
478 // If the user clicked OK or Apply then Cancel, but not only Cancel.
479 if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
480 // Start fresh.
481 ResetSettings();
483 DEVMODE* dev_mode = NULL;
484 if (dialog_options.hDevMode) {
485 dev_mode =
486 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
487 DCHECK(dev_mode);
490 std::wstring device_name;
491 if (dialog_options.hDevNames) {
492 DEVNAMES* dev_names =
493 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
494 DCHECK(dev_names);
495 if (dev_names) {
496 device_name =
497 reinterpret_cast<const wchar_t*>(
498 reinterpret_cast<const wchar_t*>(dev_names) +
499 dev_names->wDeviceOffset);
500 GlobalUnlock(dialog_options.hDevNames);
504 bool success = false;
505 if (dev_mode && !device_name.empty()) {
506 context_ = dialog_options.hDC;
507 PRINTPAGERANGE* page_ranges = NULL;
508 DWORD num_page_ranges = 0;
509 bool print_selection_only = false;
510 if (dialog_options.Flags & PD_PAGENUMS) {
511 page_ranges = dialog_options.lpPageRanges;
512 num_page_ranges = dialog_options.nPageRanges;
514 if (dialog_options.Flags & PD_SELECTION) {
515 print_selection_only = true;
517 success = InitializeSettings(*dev_mode,
518 device_name,
519 page_ranges,
520 num_page_ranges,
521 print_selection_only);
524 if (!success && dialog_options.hDC) {
525 DeleteDC(dialog_options.hDC);
526 context_ = NULL;
529 if (dev_mode) {
530 GlobalUnlock(dialog_options.hDevMode);
532 } else {
533 if (dialog_options.hDC) {
534 DeleteDC(dialog_options.hDC);
538 if (dialog_options.hDevMode != NULL)
539 GlobalFree(dialog_options.hDevMode);
540 if (dialog_options.hDevNames != NULL)
541 GlobalFree(dialog_options.hDevNames);
543 switch (dialog_options.dwResultAction) {
544 case PD_RESULT_PRINT:
545 return context_ ? OK : FAILED;
546 case PD_RESULT_APPLY:
547 return context_ ? CANCEL : FAILED;
548 case PD_RESULT_CANCEL:
549 return CANCEL;
550 default:
551 return FAILED;
555 PrintingContext::Result PrintingContextWin::ParseDialogResult(
556 const PRINTDLG& dialog_options) {
557 // If the user clicked OK or Apply then Cancel, but not only Cancel.
558 // Start fresh.
559 ResetSettings();
561 DEVMODE* dev_mode = NULL;
562 if (dialog_options.hDevMode) {
563 dev_mode =
564 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
565 DCHECK(dev_mode);
568 std::wstring device_name;
569 if (dialog_options.hDevNames) {
570 DEVNAMES* dev_names =
571 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
572 DCHECK(dev_names);
573 if (dev_names) {
574 device_name =
575 reinterpret_cast<const wchar_t*>(
576 reinterpret_cast<const wchar_t*>(dev_names) +
577 dev_names->wDeviceOffset);
578 GlobalUnlock(dialog_options.hDevNames);
582 bool success = false;
583 if (dev_mode && !device_name.empty()) {
584 context_ = dialog_options.hDC;
585 success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
588 if (!success && dialog_options.hDC) {
589 DeleteDC(dialog_options.hDC);
590 context_ = NULL;
593 if (dev_mode) {
594 GlobalUnlock(dialog_options.hDevMode);
597 if (dialog_options.hDevMode != NULL)
598 GlobalFree(dialog_options.hDevMode);
599 if (dialog_options.hDevNames != NULL)
600 GlobalFree(dialog_options.hDevNames);
602 return context_ ? OK : FAILED;
605 // static
606 void PrintingContextWin::GetPrinterHelper(HANDLE printer, int level,
607 scoped_array<uint8>* buffer) {
608 DWORD buf_size = 0;
609 GetPrinter(printer, level, NULL, 0, &buf_size);
610 if (buf_size) {
611 buffer->reset(new uint8[buf_size]);
612 memset(buffer->get(), 0, buf_size);
613 if (!GetPrinter(printer, level, buffer->get(), buf_size, &buf_size)) {
614 buffer->reset();
619 } // namespace printing