Home | History | Annotate | Download | only in printing
      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