blob: d4f0b0291906175f9aba9e226b3f7a976db1b762 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 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.
4
5#include "printing/printing_context_win.h"
6
7#include <winspool.h>
8
9#include <algorithm>
10
11#include "base/i18n/file_util_icu.h"
12#include "base/i18n/time_formatting.h"
Ben Murdoch9ab55632013-07-18 11:57:30 +010013#include "base/message_loop/message_loop.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014#include "base/metrics/histogram.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010015#include "base/strings/utf_string_conversions.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010016#include "base/time/time.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000017#include "base/values.h"
18#include "base/win/metro.h"
19#include "printing/backend/print_backend.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000020#include "printing/backend/printing_info_win.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000021#include "printing/backend/win_helper.h"
22#include "printing/print_job_constants.h"
23#include "printing/print_settings_initializer_win.h"
24#include "printing/printed_document.h"
25#include "printing/units.h"
26#include "skia/ext/platform_device.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000027#include "win8/util/win8_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000028
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010029#if defined(USE_AURA)
30#include "ui/aura/remote_root_window_host_win.h"
31#include "ui/aura/root_window.h"
32#include "ui/aura/window.h"
33#endif
34
Torne (Richard Coles)58218062012-11-14 11:43:16 +000035using base::Time;
36
37namespace {
38
39// Constants for setting default PDF settings.
40const int kPDFDpi = 300; // 300 dpi
41// LETTER: 8.5 x 11 inches
42const int kPDFLetterWidth = 8.5 * kPDFDpi;
43const int kPDFLetterHeight = 11 * kPDFDpi;
44// LEGAL: 8.5 x 14 inches
45const int kPDFLegalWidth = 8.5 * kPDFDpi;
46const int kPDFLegalHeight = 14 * kPDFDpi;
47// A4: 8.27 x 11.69 inches
48const int kPDFA4Width = 8.27 * kPDFDpi;
49const int kPDFA4Height = 11.69 * kPDFDpi;
50// A3: 11.69 x 16.54 inches
51const int kPDFA3Width = 11.69 * kPDFDpi;
52const int kPDFA3Height = 16.54 * kPDFDpi;
53
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010054HWND GetRootWindow(gfx::NativeView view) {
55 HWND window = NULL;
56#if defined(USE_AURA)
57 if (view)
58 window = view->GetRootWindow()->GetAcceleratedWidget();
59#else
60 if (view && IsWindow(view)) {
61 window = GetAncestor(view, GA_ROOTOWNER);
62 }
63#endif
64 if (!window) {
65 // TODO(maruel): bug 1214347 Get the right browser window instead.
66 return GetDesktopWindow();
67 }
68 return window;
69}
70
Torne (Richard Coles)58218062012-11-14 11:43:16 +000071} // anonymous namespace
72
73namespace printing {
74
75class PrintingContextWin::CallbackHandler : public IPrintDialogCallback,
76 public IObjectWithSite {
77 public:
78 CallbackHandler(PrintingContextWin& owner, HWND owner_hwnd)
79 : owner_(owner),
80 owner_hwnd_(owner_hwnd),
81 services_(NULL) {
82 }
83
84 ~CallbackHandler() {
85 if (services_)
86 services_->Release();
87 }
88
89 IUnknown* ToIUnknown() {
90 return static_cast<IUnknown*>(static_cast<IPrintDialogCallback*>(this));
91 }
92
93 // IUnknown
94 virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) {
95 if (riid == IID_IUnknown) {
96 *object = ToIUnknown();
97 } else if (riid == IID_IPrintDialogCallback) {
98 *object = static_cast<IPrintDialogCallback*>(this);
99 } else if (riid == IID_IObjectWithSite) {
100 *object = static_cast<IObjectWithSite*>(this);
101 } else {
102 return E_NOINTERFACE;
103 }
104 return S_OK;
105 }
106
107 // No real ref counting.
108 virtual ULONG WINAPI AddRef() {
109 return 1;
110 }
111 virtual ULONG WINAPI Release() {
112 return 1;
113 }
114
115 // IPrintDialogCallback methods
116 virtual HRESULT WINAPI InitDone() {
117 return S_OK;
118 }
119
120 virtual HRESULT WINAPI SelectionChange() {
121 if (services_) {
122 // TODO(maruel): Get the devmode for the new printer with
123 // services_->GetCurrentDevMode(&devmode, &size), send that information
124 // back to our client and continue. The client needs to recalculate the
125 // number of rendered pages and send back this information here.
126 }
127 return S_OK;
128 }
129
130 virtual HRESULT WINAPI HandleMessage(HWND dialog,
131 UINT message,
132 WPARAM wparam,
133 LPARAM lparam,
134 LRESULT* result) {
135 // Cheap way to retrieve the window handle.
136 if (!owner_.dialog_box_) {
137 // The handle we receive is the one of the groupbox in the General tab. We
138 // need to get the grand-father to get the dialog box handle.
139 owner_.dialog_box_ = GetAncestor(dialog, GA_ROOT);
140 // Trick to enable the owner window. This can cause issues with navigation
141 // events so it may have to be disabled if we don't fix the side-effects.
142 EnableWindow(owner_hwnd_, TRUE);
143 }
144 return S_FALSE;
145 }
146
147 virtual HRESULT WINAPI SetSite(IUnknown* site) {
148 if (!site) {
149 DCHECK(services_);
150 services_->Release();
151 services_ = NULL;
152 // The dialog box is destroying, PrintJob::Worker don't need the handle
153 // anymore.
154 owner_.dialog_box_ = NULL;
155 } else {
156 DCHECK(services_ == NULL);
157 HRESULT hr = site->QueryInterface(IID_IPrintDialogServices,
158 reinterpret_cast<void**>(&services_));
159 DCHECK(SUCCEEDED(hr));
160 }
161 return S_OK;
162 }
163
164 virtual HRESULT WINAPI GetSite(REFIID riid, void** site) {
165 return E_NOTIMPL;
166 }
167
168 private:
169 PrintingContextWin& owner_;
170 HWND owner_hwnd_;
171 IPrintDialogServices* services_;
172
173 DISALLOW_COPY_AND_ASSIGN(CallbackHandler);
174};
175
176// static
177PrintingContext* PrintingContext::Create(const std::string& app_locale) {
178 return static_cast<PrintingContext*>(new PrintingContextWin(app_locale));
179}
180
181PrintingContextWin::PrintingContextWin(const std::string& app_locale)
182 : PrintingContext(app_locale),
183 context_(NULL),
184 dialog_box_(NULL),
185 print_dialog_func_(&PrintDlgEx) {
186}
187
188PrintingContextWin::~PrintingContextWin() {
189 ReleaseContext();
190}
191
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100192// TODO(vitalybuka): Implement as ui::BaseShellDialog crbug.com/180997.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000193void PrintingContextWin::AskUserForSettings(
194 gfx::NativeView view, int max_pages, bool has_selection,
195 const PrintSettingsCallback& callback) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000196 DCHECK(!in_print_job_);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000197 if (win8::IsSingleWindowMetroMode()) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000198 // The system dialog can not be opened while running in Metro.
199 // But we can programatically launch the Metro print device charm though.
200 HMODULE metro_module = base::win::GetMetroModule();
201 if (metro_module != NULL) {
202 typedef void (*MetroShowPrintUI)();
203 MetroShowPrintUI metro_show_print_ui =
204 reinterpret_cast<MetroShowPrintUI>(
205 ::GetProcAddress(metro_module, "MetroShowPrintUI"));
206 if (metro_show_print_ui) {
207 // TODO(mad): Remove this once we can send user metrics from the metro
208 // driver. crbug.com/142330
209 UMA_HISTOGRAM_ENUMERATION("Metro.Print", 1, 2);
210 metro_show_print_ui();
211 }
212 }
213 return callback.Run(CANCEL);
214 }
215 dialog_box_dismissed_ = false;
216
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100217 HWND window = GetRootWindow(view);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000218 DCHECK(window);
219
220 // Show the OS-dependent dialog box.
221 // If the user press
222 // - OK, the settings are reset and reinitialized with the new settings. OK is
223 // returned.
224 // - Apply then Cancel, the settings are reset and reinitialized with the new
225 // settings. CANCEL is returned.
226 // - Cancel, the settings are not changed, the previous setting, if it was
227 // initialized before, are kept. CANCEL is returned.
228 // On failure, the settings are reset and FAILED is returned.
229 PRINTDLGEX dialog_options = { sizeof(PRINTDLGEX) };
230 dialog_options.hwndOwner = window;
231 // Disable options we don't support currently.
232 // TODO(maruel): Reuse the previously loaded settings!
233 dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
234 PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
235 if (!has_selection)
236 dialog_options.Flags |= PD_NOSELECTION;
237
238 PRINTPAGERANGE ranges[32];
239 dialog_options.nStartPage = START_PAGE_GENERAL;
240 if (max_pages) {
241 // Default initialize to print all the pages.
242 memset(ranges, 0, sizeof(ranges));
243 ranges[0].nFromPage = 1;
244 ranges[0].nToPage = max_pages;
245 dialog_options.nPageRanges = 1;
246 dialog_options.nMaxPageRanges = arraysize(ranges);
247 dialog_options.nMinPage = 1;
248 dialog_options.nMaxPage = max_pages;
249 dialog_options.lpPageRanges = ranges;
250 } else {
251 // No need to bother, we don't know how many pages are available.
252 dialog_options.Flags |= PD_NOPAGENUMS;
253 }
254
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100255 HRESULT hr = (*print_dialog_func_)(&dialog_options);
256 if (hr != S_OK) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000257 ResetSettings();
258 callback.Run(FAILED);
259 }
260
261 // TODO(maruel): Support PD_PRINTTOFILE.
262 callback.Run(ParseDialogResultEx(dialog_options));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000263}
264
265PrintingContext::Result PrintingContextWin::UseDefaultSettings() {
266 DCHECK(!in_print_job_);
267
268 PRINTDLG dialog_options = { sizeof(PRINTDLG) };
269 dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
270 if (PrintDlg(&dialog_options))
271 return ParseDialogResult(dialog_options);
272
273 // No default printer configured, do we have any printers at all?
274 DWORD bytes_needed = 0;
275 DWORD count_returned = 0;
276 (void)::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
277 NULL, 2, NULL, 0, &bytes_needed, &count_returned);
278 if (bytes_needed) {
279 DCHECK(bytes_needed >= count_returned * sizeof(PRINTER_INFO_2));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100280 scoped_ptr<BYTE[]> printer_info_buffer(new BYTE[bytes_needed]);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000281 BOOL ret = ::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
282 NULL, 2, printer_info_buffer.get(),
283 bytes_needed, &bytes_needed,
284 &count_returned);
285 if (ret && count_returned) { // have printers
286 // Open the first successfully found printer.
287 for (DWORD count = 0; count < count_returned; ++count) {
288 PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(
289 printer_info_buffer.get() + count * sizeof(PRINTER_INFO_2));
290 std::wstring printer_name = info_2->pPrinterName;
291 if (info_2->pDevMode == NULL || printer_name.length() == 0)
292 continue;
293 if (!AllocateContext(printer_name, info_2->pDevMode, &context_))
294 break;
295 if (InitializeSettings(*info_2->pDevMode, printer_name,
296 NULL, 0, false)) {
297 break;
298 }
299 ReleaseContext();
300 }
301 if (context_)
302 return OK;
303 }
304 }
305
306 ResetSettings();
307 return FAILED;
308}
309
310PrintingContext::Result PrintingContextWin::UpdatePrinterSettings(
311 const DictionaryValue& job_settings,
312 const PageRanges& ranges) {
313 DCHECK(!in_print_job_);
314
315 bool collate;
316 int color;
317 bool landscape;
318 bool print_to_pdf;
319 bool is_cloud_dialog;
320 int copies;
321 int duplex_mode;
322 string16 device_name;
323
324 if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
325 !job_settings.GetBoolean(kSettingCollate, &collate) ||
326 !job_settings.GetInteger(kSettingColor, &color) ||
327 !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
328 !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
329 !job_settings.GetInteger(kSettingCopies, &copies) ||
330 !job_settings.GetString(kSettingDeviceName, &device_name) ||
331 !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
332 return OnError();
333 }
334
335 bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
336
337 if (print_to_pdf || print_to_cloud || is_cloud_dialog) {
338 // Default fallback to Letter size.
339 gfx::Size paper_size;
340 gfx::Rect paper_rect;
341 paper_size.SetSize(kPDFLetterWidth, kPDFLetterHeight);
342
343 // Get settings from locale. Paper type buffer length is at most 4.
344 const int paper_type_buffer_len = 4;
345 wchar_t paper_type_buffer[paper_type_buffer_len] = {0};
346 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE, paper_type_buffer,
347 paper_type_buffer_len);
348 if (wcslen(paper_type_buffer)) { // The call succeeded.
349 int paper_code = _wtoi(paper_type_buffer);
350 switch (paper_code) {
351 case DMPAPER_LEGAL:
352 paper_size.SetSize(kPDFLegalWidth, kPDFLegalHeight);
353 break;
354 case DMPAPER_A4:
355 paper_size.SetSize(kPDFA4Width, kPDFA4Height);
356 break;
357 case DMPAPER_A3:
358 paper_size.SetSize(kPDFA3Width, kPDFA3Height);
359 break;
360 default: // DMPAPER_LETTER is used for default fallback.
361 break;
362 }
363 }
364 paper_rect.SetRect(0, 0, paper_size.width(), paper_size.height());
365 settings_.SetPrinterPrintableArea(paper_size, paper_rect, kPDFDpi);
366 settings_.set_dpi(kPDFDpi);
367 settings_.SetOrientation(landscape);
368 settings_.ranges = ranges;
369 return OK;
370 }
371
372 ScopedPrinterHandle printer;
373 LPWSTR device_name_wide = const_cast<wchar_t*>(device_name.c_str());
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100374 if (!printer.OpenPrinter(device_name_wide))
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000375 return OnError();
376
377 // Make printer changes local to Chrome.
378 // See MSDN documentation regarding DocumentProperties.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100379 scoped_ptr<uint8[]> buffer;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000380 DEVMODE* dev_mode = NULL;
381 LONG buffer_size = DocumentProperties(NULL, printer, device_name_wide,
382 NULL, NULL, 0);
383 if (buffer_size > 0) {
384 buffer.reset(new uint8[buffer_size]);
385 memset(buffer.get(), 0, buffer_size);
386 if (DocumentProperties(NULL, printer, device_name_wide,
387 reinterpret_cast<PDEVMODE>(buffer.get()), NULL,
388 DM_OUT_BUFFER) == IDOK) {
389 dev_mode = reinterpret_cast<PDEVMODE>(buffer.get());
390 }
391 }
392 if (dev_mode == NULL) {
393 buffer.reset();
394 return OnError();
395 }
396
397 if (color == GRAY)
398 dev_mode->dmColor = DMCOLOR_MONOCHROME;
399 else
400 dev_mode->dmColor = DMCOLOR_COLOR;
401
402 dev_mode->dmCopies = std::max(copies, 1);
403 if (dev_mode->dmCopies > 1) // do not change collate unless multiple copies
404 dev_mode->dmCollate = collate ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
405 switch (duplex_mode) {
406 case LONG_EDGE:
407 dev_mode->dmDuplex = DMDUP_VERTICAL;
408 break;
409 case SHORT_EDGE:
410 dev_mode->dmDuplex = DMDUP_HORIZONTAL;
411 break;
412 case SIMPLEX:
413 dev_mode->dmDuplex = DMDUP_SIMPLEX;
414 break;
415 default: // UNKNOWN_DUPLEX_MODE
416 break;
417 }
418 dev_mode->dmOrientation = landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
419
420 // Update data using DocumentProperties.
421 if (DocumentProperties(NULL, printer, device_name_wide, dev_mode, dev_mode,
422 DM_IN_BUFFER | DM_OUT_BUFFER) != IDOK) {
423 return OnError();
424 }
425
426 // Set printer then refresh printer settings.
427 if (!AllocateContext(device_name, dev_mode, &context_)) {
428 return OnError();
429 }
430 PrintSettingsInitializerWin::InitPrintSettings(context_, *dev_mode,
431 ranges, device_name,
432 false, &settings_);
433 return OK;
434}
435
436PrintingContext::Result PrintingContextWin::InitWithSettings(
437 const PrintSettings& settings) {
438 DCHECK(!in_print_job_);
439
440 settings_ = settings;
441
442 // TODO(maruel): settings_.ToDEVMODE()
443 ScopedPrinterHandle printer;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100444 if (!printer.OpenPrinter(settings_.device_name().c_str())) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000445 return FAILED;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100446 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000447
448 Result status = OK;
449
450 if (!GetPrinterSettings(printer, settings_.device_name()))
451 status = FAILED;
452
453 if (status != OK)
454 ResetSettings();
455 return status;
456}
457
458PrintingContext::Result PrintingContextWin::NewDocument(
459 const string16& document_name) {
460 DCHECK(!in_print_job_);
461 if (!context_)
462 return OnError();
463
464 // Set the flag used by the AbortPrintJob dialog procedure.
465 abort_printing_ = false;
466
467 in_print_job_ = true;
468
469 // Register the application's AbortProc function with GDI.
470 if (SP_ERROR == SetAbortProc(context_, &AbortProc))
471 return OnError();
472
473 DCHECK(PrintBackend::SimplifyDocumentTitle(document_name) == document_name);
474 DOCINFO di = { sizeof(DOCINFO) };
475 const std::wstring& document_name_wide = UTF16ToWide(document_name);
476 di.lpszDocName = document_name_wide.c_str();
477
478 // Is there a debug dump directory specified? If so, force to print to a file.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000479 base::FilePath debug_dump_path = PrintedDocument::debug_dump_path();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000480 if (!debug_dump_path.empty()) {
481 // Create a filename.
482 std::wstring filename;
483 Time now(Time::Now());
484 filename = base::TimeFormatShortDateNumeric(now);
485 filename += L"_";
486 filename += base::TimeFormatTimeOfDay(now);
487 filename += L"_";
488 filename += UTF16ToWide(document_name);
489 filename += L"_";
490 filename += L"buffer.prn";
491 file_util::ReplaceIllegalCharactersInPath(&filename, '_');
492 debug_dump_path.Append(filename);
493 di.lpszOutput = debug_dump_path.value().c_str();
494 }
495
496 // No message loop running in unit tests.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100497 DCHECK(!base::MessageLoop::current() ||
498 !base::MessageLoop::current()->NestableTasksAllowed());
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000499
500 // Begin a print job by calling the StartDoc function.
501 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
502 // IPC. Make sure recursive task processing is disabled.
503 if (StartDoc(context_, &di) <= 0)
504 return OnError();
505
506 return OK;
507}
508
509PrintingContext::Result PrintingContextWin::NewPage() {
510 if (abort_printing_)
511 return CANCEL;
512 DCHECK(context_);
513 DCHECK(in_print_job_);
514
515 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
516 // ::StartPage().
517
518 return OK;
519}
520
521PrintingContext::Result PrintingContextWin::PageDone() {
522 if (abort_printing_)
523 return CANCEL;
524 DCHECK(in_print_job_);
525
526 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
527 // ::EndPage().
528
529 return OK;
530}
531
532PrintingContext::Result PrintingContextWin::DocumentDone() {
533 if (abort_printing_)
534 return CANCEL;
535 DCHECK(in_print_job_);
536 DCHECK(context_);
537
538 // Inform the driver that document has ended.
539 if (EndDoc(context_) <= 0)
540 return OnError();
541
542 ResetSettings();
543 return OK;
544}
545
546void PrintingContextWin::Cancel() {
547 abort_printing_ = true;
548 in_print_job_ = false;
549 if (context_)
550 CancelDC(context_);
551 if (dialog_box_) {
552 DestroyWindow(dialog_box_);
553 dialog_box_dismissed_ = true;
554 }
555}
556
557void PrintingContextWin::ReleaseContext() {
558 if (context_) {
559 DeleteDC(context_);
560 context_ = NULL;
561 }
562}
563
564gfx::NativeDrawingContext PrintingContextWin::context() const {
565 return context_;
566}
567
568// static
569BOOL PrintingContextWin::AbortProc(HDC hdc, int nCode) {
570 if (nCode) {
571 // TODO(maruel): Need a way to find the right instance to set. Should
572 // leverage PrintJobManager here?
573 // abort_printing_ = true;
574 }
575 return true;
576}
577
578bool PrintingContextWin::InitializeSettings(const DEVMODE& dev_mode,
579 const std::wstring& new_device_name,
580 const PRINTPAGERANGE* ranges,
581 int number_ranges,
582 bool selection_only) {
583 skia::InitializeDC(context_);
584 DCHECK(GetDeviceCaps(context_, CLIPCAPS));
585 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB);
586 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64);
587 // Some printers don't advertise these.
588 // DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_SCALING);
589 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_CONST_ALPHA);
590 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
591
592 // StretchDIBits() support is needed for printing.
593 if (!(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB) ||
594 !(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64)) {
595 NOTREACHED();
596 ResetSettings();
597 return false;
598 }
599
600 DCHECK(!in_print_job_);
601 DCHECK(context_);
602 PageRanges ranges_vector;
603 if (!selection_only) {
604 // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
605 ranges_vector.reserve(number_ranges);
606 for (int i = 0; i < number_ranges; ++i) {
607 PageRange range;
608 // Transfer from 1-based to 0-based.
609 range.from = ranges[i].nFromPage - 1;
610 range.to = ranges[i].nToPage - 1;
611 ranges_vector.push_back(range);
612 }
613 }
614
615 PrintSettingsInitializerWin::InitPrintSettings(context_,
616 dev_mode,
617 ranges_vector,
618 new_device_name,
619 selection_only,
620 &settings_);
621
622 return true;
623}
624
625bool PrintingContextWin::GetPrinterSettings(HANDLE printer,
626 const std::wstring& device_name) {
627 DCHECK(!in_print_job_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000628
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000629 UserDefaultDevMode user_settings;
630
631 if (!user_settings.Init(printer) ||
632 !AllocateContext(device_name, user_settings.get(), &context_)) {
633 ResetSettings();
634 return false;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000635 }
636
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000637 return InitializeSettings(*user_settings.get(), device_name, NULL, 0, false);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000638}
639
640// static
641bool PrintingContextWin::AllocateContext(const std::wstring& device_name,
642 const DEVMODE* dev_mode,
643 gfx::NativeDrawingContext* context) {
644 *context = CreateDC(L"WINSPOOL", device_name.c_str(), NULL, dev_mode);
645 DCHECK(*context);
646 return *context != NULL;
647}
648
649PrintingContext::Result PrintingContextWin::ParseDialogResultEx(
650 const PRINTDLGEX& dialog_options) {
651 // If the user clicked OK or Apply then Cancel, but not only Cancel.
652 if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
653 // Start fresh.
654 ResetSettings();
655
656 DEVMODE* dev_mode = NULL;
657 if (dialog_options.hDevMode) {
658 dev_mode =
659 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
660 DCHECK(dev_mode);
661 }
662
663 std::wstring device_name;
664 if (dialog_options.hDevNames) {
665 DEVNAMES* dev_names =
666 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
667 DCHECK(dev_names);
668 if (dev_names) {
669 device_name =
670 reinterpret_cast<const wchar_t*>(
671 reinterpret_cast<const wchar_t*>(dev_names) +
672 dev_names->wDeviceOffset);
673 GlobalUnlock(dialog_options.hDevNames);
674 }
675 }
676
677 bool success = false;
678 if (dev_mode && !device_name.empty()) {
679 context_ = dialog_options.hDC;
680 PRINTPAGERANGE* page_ranges = NULL;
681 DWORD num_page_ranges = 0;
682 bool print_selection_only = false;
683 if (dialog_options.Flags & PD_PAGENUMS) {
684 page_ranges = dialog_options.lpPageRanges;
685 num_page_ranges = dialog_options.nPageRanges;
686 }
687 if (dialog_options.Flags & PD_SELECTION) {
688 print_selection_only = true;
689 }
690 success = InitializeSettings(*dev_mode,
691 device_name,
692 page_ranges,
693 num_page_ranges,
694 print_selection_only);
695 }
696
697 if (!success && dialog_options.hDC) {
698 DeleteDC(dialog_options.hDC);
699 context_ = NULL;
700 }
701
702 if (dev_mode) {
703 GlobalUnlock(dialog_options.hDevMode);
704 }
705 } else {
706 if (dialog_options.hDC) {
707 DeleteDC(dialog_options.hDC);
708 }
709 }
710
711 if (dialog_options.hDevMode != NULL)
712 GlobalFree(dialog_options.hDevMode);
713 if (dialog_options.hDevNames != NULL)
714 GlobalFree(dialog_options.hDevNames);
715
716 switch (dialog_options.dwResultAction) {
717 case PD_RESULT_PRINT:
718 return context_ ? OK : FAILED;
719 case PD_RESULT_APPLY:
720 return context_ ? CANCEL : FAILED;
721 case PD_RESULT_CANCEL:
722 return CANCEL;
723 default:
724 return FAILED;
725 }
726}
727
728PrintingContext::Result PrintingContextWin::ParseDialogResult(
729 const PRINTDLG& dialog_options) {
730 // If the user clicked OK or Apply then Cancel, but not only Cancel.
731 // Start fresh.
732 ResetSettings();
733
734 DEVMODE* dev_mode = NULL;
735 if (dialog_options.hDevMode) {
736 dev_mode =
737 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
738 DCHECK(dev_mode);
739 }
740
741 std::wstring device_name;
742 if (dialog_options.hDevNames) {
743 DEVNAMES* dev_names =
744 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
745 DCHECK(dev_names);
746 if (dev_names) {
747 device_name =
748 reinterpret_cast<const wchar_t*>(
749 reinterpret_cast<const wchar_t*>(dev_names) +
750 dev_names->wDeviceOffset);
751 GlobalUnlock(dialog_options.hDevNames);
752 }
753 }
754
755 bool success = false;
756 if (dev_mode && !device_name.empty()) {
757 context_ = dialog_options.hDC;
758 success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
759 }
760
761 if (!success && dialog_options.hDC) {
762 DeleteDC(dialog_options.hDC);
763 context_ = NULL;
764 }
765
766 if (dev_mode) {
767 GlobalUnlock(dialog_options.hDevMode);
768 }
769
770 if (dialog_options.hDevMode != NULL)
771 GlobalFree(dialog_options.hDevMode);
772 if (dialog_options.hDevNames != NULL)
773 GlobalFree(dialog_options.hDevNames);
774
775 return context_ ? OK : FAILED;
776}
777
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000778} // namespace printing