1 // 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" 13 #include "base/message_loop/message_loop.h" 14 #include "base/metrics/histogram.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "base/time/time.h" 17 #include "base/values.h" 18 #include "base/win/metro.h" 19 #include "printing/backend/print_backend.h" 20 #include "printing/backend/printing_info_win.h" 21 #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" 27 #include "win8/util/win8_util.h" 28 29 #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 35 using base::Time; 36 37 namespace { 38 39 // Constants for setting default PDF settings. 40 const int kPDFDpi = 300; // 300 dpi 41 // LETTER: 8.5 x 11 inches 42 const int kPDFLetterWidth = 8.5 * kPDFDpi; 43 const int kPDFLetterHeight = 11 * kPDFDpi; 44 // LEGAL: 8.5 x 14 inches 45 const int kPDFLegalWidth = 8.5 * kPDFDpi; 46 const int kPDFLegalHeight = 14 * kPDFDpi; 47 // A4: 8.27 x 11.69 inches 48 const int kPDFA4Width = 8.27 * kPDFDpi; 49 const int kPDFA4Height = 11.69 * kPDFDpi; 50 // A3: 11.69 x 16.54 inches 51 const int kPDFA3Width = 11.69 * kPDFDpi; 52 const int kPDFA3Height = 16.54 * kPDFDpi; 53 54 HWND 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 71 } // anonymous namespace 72 73 namespace printing { 74 75 class 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 177 PrintingContext* PrintingContext::Create(const std::string& app_locale) { 178 return static_cast<PrintingContext*>(new PrintingContextWin(app_locale)); 179 } 180 181 PrintingContextWin::PrintingContextWin(const std::string& app_locale) 182 : PrintingContext(app_locale), 183 context_(NULL), 184 dialog_box_(NULL), 185 print_dialog_func_(&PrintDlgEx) { 186 } 187 188 PrintingContextWin::~PrintingContextWin() { 189 ReleaseContext(); 190 } 191 192 // TODO(vitalybuka): Implement as ui::BaseShellDialog crbug.com/180997. 193 void PrintingContextWin::AskUserForSettings( 194 gfx::NativeView view, int max_pages, bool has_selection, 195 const PrintSettingsCallback& callback) { 196 DCHECK(!in_print_job_); 197 if (win8::IsSingleWindowMetroMode()) { 198 // 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 217 HWND window = GetRootWindow(view); 218 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 255 HRESULT hr = (*print_dialog_func_)(&dialog_options); 256 if (hr != S_OK) { 257 ResetSettings(); 258 callback.Run(FAILED); 259 } 260 261 // TODO(maruel): Support PD_PRINTTOFILE. 262 callback.Run(ParseDialogResultEx(dialog_options)); 263 } 264 265 PrintingContext::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)); 280 scoped_ptr<BYTE[]> printer_info_buffer(new BYTE[bytes_needed]); 281 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 310 PrintingContext::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()); 374 if (!printer.OpenPrinter(device_name_wide)) 375 return OnError(); 376 377 // Make printer changes local to Chrome. 378 // See MSDN documentation regarding DocumentProperties. 379 scoped_ptr<uint8[]> buffer; 380 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 436 PrintingContext::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; 444 if (!printer.OpenPrinter(settings_.device_name().c_str())) { 445 return FAILED; 446 } 447 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 458 PrintingContext::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. 479 base::FilePath debug_dump_path = PrintedDocument::debug_dump_path(); 480 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. 497 DCHECK(!base::MessageLoop::current() || 498 !base::MessageLoop::current()->NestableTasksAllowed()); 499 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 509 PrintingContext::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 521 PrintingContext::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 532 PrintingContext::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 546 void 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 557 void PrintingContextWin::ReleaseContext() { 558 if (context_) { 559 DeleteDC(context_); 560 context_ = NULL; 561 } 562 } 563 564 gfx::NativeDrawingContext PrintingContextWin::context() const { 565 return context_; 566 } 567 568 // static 569 BOOL 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 578 bool 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 625 bool PrintingContextWin::GetPrinterSettings(HANDLE printer, 626 const std::wstring& device_name) { 627 DCHECK(!in_print_job_); 628 629 UserDefaultDevMode user_settings; 630 631 if (!user_settings.Init(printer) || 632 !AllocateContext(device_name, user_settings.get(), &context_)) { 633 ResetSettings(); 634 return false; 635 } 636 637 return InitializeSettings(*user_settings.get(), device_name, NULL, 0, false); 638 } 639 640 // static 641 bool 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 649 PrintingContext::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 728 PrintingContext::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 778 } // namespace printing 779