summaryrefslogtreecommitdiff
path: root/chromium/printing/printing_context_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/printing/printing_context_win.cc')
-rw-r--r--chromium/printing/printing_context_win.cc778
1 files changed, 778 insertions, 0 deletions
diff --git a/chromium/printing/printing_context_win.cc b/chromium/printing/printing_context_win.cc
new file mode 100644
index 00000000000..d4f0b029190
--- /dev/null
+++ b/chromium/printing/printing_context_win.cc
@@ -0,0 +1,778 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printing_context_win.h"
+
+#include <winspool.h>
+
+#include <algorithm>
+
+#include "base/i18n/file_util_icu.h"
+#include "base/i18n/time_formatting.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "base/win/metro.h"
+#include "printing/backend/print_backend.h"
+#include "printing/backend/printing_info_win.h"
+#include "printing/backend/win_helper.h"
+#include "printing/print_job_constants.h"
+#include "printing/print_settings_initializer_win.h"
+#include "printing/printed_document.h"
+#include "printing/units.h"
+#include "skia/ext/platform_device.h"
+#include "win8/util/win8_util.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/remote_root_window_host_win.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#endif
+
+using base::Time;
+
+namespace {
+
+// Constants for setting default PDF settings.
+const int kPDFDpi = 300; // 300 dpi
+// LETTER: 8.5 x 11 inches
+const int kPDFLetterWidth = 8.5 * kPDFDpi;
+const int kPDFLetterHeight = 11 * kPDFDpi;
+// LEGAL: 8.5 x 14 inches
+const int kPDFLegalWidth = 8.5 * kPDFDpi;
+const int kPDFLegalHeight = 14 * kPDFDpi;
+// A4: 8.27 x 11.69 inches
+const int kPDFA4Width = 8.27 * kPDFDpi;
+const int kPDFA4Height = 11.69 * kPDFDpi;
+// A3: 11.69 x 16.54 inches
+const int kPDFA3Width = 11.69 * kPDFDpi;
+const int kPDFA3Height = 16.54 * kPDFDpi;
+
+HWND GetRootWindow(gfx::NativeView view) {
+ HWND window = NULL;
+#if defined(USE_AURA)
+ if (view)
+ window = view->GetRootWindow()->GetAcceleratedWidget();
+#else
+ if (view && IsWindow(view)) {
+ window = GetAncestor(view, GA_ROOTOWNER);
+ }
+#endif
+ if (!window) {
+ // TODO(maruel): bug 1214347 Get the right browser window instead.
+ return GetDesktopWindow();
+ }
+ return window;
+}
+
+} // anonymous namespace
+
+namespace printing {
+
+class PrintingContextWin::CallbackHandler : public IPrintDialogCallback,
+ public IObjectWithSite {
+ public:
+ CallbackHandler(PrintingContextWin& owner, HWND owner_hwnd)
+ : owner_(owner),
+ owner_hwnd_(owner_hwnd),
+ services_(NULL) {
+ }
+
+ ~CallbackHandler() {
+ if (services_)
+ services_->Release();
+ }
+
+ IUnknown* ToIUnknown() {
+ return static_cast<IUnknown*>(static_cast<IPrintDialogCallback*>(this));
+ }
+
+ // IUnknown
+ virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) {
+ if (riid == IID_IUnknown) {
+ *object = ToIUnknown();
+ } else if (riid == IID_IPrintDialogCallback) {
+ *object = static_cast<IPrintDialogCallback*>(this);
+ } else if (riid == IID_IObjectWithSite) {
+ *object = static_cast<IObjectWithSite*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+ }
+
+ // No real ref counting.
+ virtual ULONG WINAPI AddRef() {
+ return 1;
+ }
+ virtual ULONG WINAPI Release() {
+ return 1;
+ }
+
+ // IPrintDialogCallback methods
+ virtual HRESULT WINAPI InitDone() {
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI SelectionChange() {
+ if (services_) {
+ // TODO(maruel): Get the devmode for the new printer with
+ // services_->GetCurrentDevMode(&devmode, &size), send that information
+ // back to our client and continue. The client needs to recalculate the
+ // number of rendered pages and send back this information here.
+ }
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI HandleMessage(HWND dialog,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT* result) {
+ // Cheap way to retrieve the window handle.
+ if (!owner_.dialog_box_) {
+ // The handle we receive is the one of the groupbox in the General tab. We
+ // need to get the grand-father to get the dialog box handle.
+ owner_.dialog_box_ = GetAncestor(dialog, GA_ROOT);
+ // Trick to enable the owner window. This can cause issues with navigation
+ // events so it may have to be disabled if we don't fix the side-effects.
+ EnableWindow(owner_hwnd_, TRUE);
+ }
+ return S_FALSE;
+ }
+
+ virtual HRESULT WINAPI SetSite(IUnknown* site) {
+ if (!site) {
+ DCHECK(services_);
+ services_->Release();
+ services_ = NULL;
+ // The dialog box is destroying, PrintJob::Worker don't need the handle
+ // anymore.
+ owner_.dialog_box_ = NULL;
+ } else {
+ DCHECK(services_ == NULL);
+ HRESULT hr = site->QueryInterface(IID_IPrintDialogServices,
+ reinterpret_cast<void**>(&services_));
+ DCHECK(SUCCEEDED(hr));
+ }
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI GetSite(REFIID riid, void** site) {
+ return E_NOTIMPL;
+ }
+
+ private:
+ PrintingContextWin& owner_;
+ HWND owner_hwnd_;
+ IPrintDialogServices* services_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackHandler);
+};
+
+// static
+PrintingContext* PrintingContext::Create(const std::string& app_locale) {
+ return static_cast<PrintingContext*>(new PrintingContextWin(app_locale));
+}
+
+PrintingContextWin::PrintingContextWin(const std::string& app_locale)
+ : PrintingContext(app_locale),
+ context_(NULL),
+ dialog_box_(NULL),
+ print_dialog_func_(&PrintDlgEx) {
+}
+
+PrintingContextWin::~PrintingContextWin() {
+ ReleaseContext();
+}
+
+// TODO(vitalybuka): Implement as ui::BaseShellDialog crbug.com/180997.
+void PrintingContextWin::AskUserForSettings(
+ gfx::NativeView view, int max_pages, bool has_selection,
+ const PrintSettingsCallback& callback) {
+ DCHECK(!in_print_job_);
+ if (win8::IsSingleWindowMetroMode()) {
+ // The system dialog can not be opened while running in Metro.
+ // But we can programatically launch the Metro print device charm though.
+ HMODULE metro_module = base::win::GetMetroModule();
+ if (metro_module != NULL) {
+ typedef void (*MetroShowPrintUI)();
+ MetroShowPrintUI metro_show_print_ui =
+ reinterpret_cast<MetroShowPrintUI>(
+ ::GetProcAddress(metro_module, "MetroShowPrintUI"));
+ if (metro_show_print_ui) {
+ // TODO(mad): Remove this once we can send user metrics from the metro
+ // driver. crbug.com/142330
+ UMA_HISTOGRAM_ENUMERATION("Metro.Print", 1, 2);
+ metro_show_print_ui();
+ }
+ }
+ return callback.Run(CANCEL);
+ }
+ dialog_box_dismissed_ = false;
+
+ HWND window = GetRootWindow(view);
+ DCHECK(window);
+
+ // Show the OS-dependent dialog box.
+ // If the user press
+ // - OK, the settings are reset and reinitialized with the new settings. OK is
+ // returned.
+ // - Apply then Cancel, the settings are reset and reinitialized with the new
+ // settings. CANCEL is returned.
+ // - Cancel, the settings are not changed, the previous setting, if it was
+ // initialized before, are kept. CANCEL is returned.
+ // On failure, the settings are reset and FAILED is returned.
+ PRINTDLGEX dialog_options = { sizeof(PRINTDLGEX) };
+ dialog_options.hwndOwner = window;
+ // Disable options we don't support currently.
+ // TODO(maruel): Reuse the previously loaded settings!
+ dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
+ PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
+ if (!has_selection)
+ dialog_options.Flags |= PD_NOSELECTION;
+
+ PRINTPAGERANGE ranges[32];
+ dialog_options.nStartPage = START_PAGE_GENERAL;
+ if (max_pages) {
+ // Default initialize to print all the pages.
+ memset(ranges, 0, sizeof(ranges));
+ ranges[0].nFromPage = 1;
+ ranges[0].nToPage = max_pages;
+ dialog_options.nPageRanges = 1;
+ dialog_options.nMaxPageRanges = arraysize(ranges);
+ dialog_options.nMinPage = 1;
+ dialog_options.nMaxPage = max_pages;
+ dialog_options.lpPageRanges = ranges;
+ } else {
+ // No need to bother, we don't know how many pages are available.
+ dialog_options.Flags |= PD_NOPAGENUMS;
+ }
+
+ HRESULT hr = (*print_dialog_func_)(&dialog_options);
+ if (hr != S_OK) {
+ ResetSettings();
+ callback.Run(FAILED);
+ }
+
+ // TODO(maruel): Support PD_PRINTTOFILE.
+ callback.Run(ParseDialogResultEx(dialog_options));
+}
+
+PrintingContext::Result PrintingContextWin::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ PRINTDLG dialog_options = { sizeof(PRINTDLG) };
+ dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
+ if (PrintDlg(&dialog_options))
+ return ParseDialogResult(dialog_options);
+
+ // No default printer configured, do we have any printers at all?
+ DWORD bytes_needed = 0;
+ DWORD count_returned = 0;
+ (void)::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
+ NULL, 2, NULL, 0, &bytes_needed, &count_returned);
+ if (bytes_needed) {
+ DCHECK(bytes_needed >= count_returned * sizeof(PRINTER_INFO_2));
+ scoped_ptr<BYTE[]> printer_info_buffer(new BYTE[bytes_needed]);
+ BOOL ret = ::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
+ NULL, 2, printer_info_buffer.get(),
+ bytes_needed, &bytes_needed,
+ &count_returned);
+ if (ret && count_returned) { // have printers
+ // Open the first successfully found printer.
+ for (DWORD count = 0; count < count_returned; ++count) {
+ PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(
+ printer_info_buffer.get() + count * sizeof(PRINTER_INFO_2));
+ std::wstring printer_name = info_2->pPrinterName;
+ if (info_2->pDevMode == NULL || printer_name.length() == 0)
+ continue;
+ if (!AllocateContext(printer_name, info_2->pDevMode, &context_))
+ break;
+ if (InitializeSettings(*info_2->pDevMode, printer_name,
+ NULL, 0, false)) {
+ break;
+ }
+ ReleaseContext();
+ }
+ if (context_)
+ return OK;
+ }
+ }
+
+ ResetSettings();
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContextWin::UpdatePrinterSettings(
+ const DictionaryValue& job_settings,
+ const PageRanges& ranges) {
+ DCHECK(!in_print_job_);
+
+ bool collate;
+ int color;
+ bool landscape;
+ bool print_to_pdf;
+ bool is_cloud_dialog;
+ int copies;
+ int duplex_mode;
+ string16 device_name;
+
+ if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
+ !job_settings.GetBoolean(kSettingCollate, &collate) ||
+ !job_settings.GetInteger(kSettingColor, &color) ||
+ !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
+ !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
+ !job_settings.GetInteger(kSettingCopies, &copies) ||
+ !job_settings.GetString(kSettingDeviceName, &device_name) ||
+ !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
+ return OnError();
+ }
+
+ bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
+
+ if (print_to_pdf || print_to_cloud || is_cloud_dialog) {
+ // Default fallback to Letter size.
+ gfx::Size paper_size;
+ gfx::Rect paper_rect;
+ paper_size.SetSize(kPDFLetterWidth, kPDFLetterHeight);
+
+ // Get settings from locale. Paper type buffer length is at most 4.
+ const int paper_type_buffer_len = 4;
+ wchar_t paper_type_buffer[paper_type_buffer_len] = {0};
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE, paper_type_buffer,
+ paper_type_buffer_len);
+ if (wcslen(paper_type_buffer)) { // The call succeeded.
+ int paper_code = _wtoi(paper_type_buffer);
+ switch (paper_code) {
+ case DMPAPER_LEGAL:
+ paper_size.SetSize(kPDFLegalWidth, kPDFLegalHeight);
+ break;
+ case DMPAPER_A4:
+ paper_size.SetSize(kPDFA4Width, kPDFA4Height);
+ break;
+ case DMPAPER_A3:
+ paper_size.SetSize(kPDFA3Width, kPDFA3Height);
+ break;
+ default: // DMPAPER_LETTER is used for default fallback.
+ break;
+ }
+ }
+ paper_rect.SetRect(0, 0, paper_size.width(), paper_size.height());
+ settings_.SetPrinterPrintableArea(paper_size, paper_rect, kPDFDpi);
+ settings_.set_dpi(kPDFDpi);
+ settings_.SetOrientation(landscape);
+ settings_.ranges = ranges;
+ return OK;
+ }
+
+ ScopedPrinterHandle printer;
+ LPWSTR device_name_wide = const_cast<wchar_t*>(device_name.c_str());
+ if (!printer.OpenPrinter(device_name_wide))
+ return OnError();
+
+ // Make printer changes local to Chrome.
+ // See MSDN documentation regarding DocumentProperties.
+ scoped_ptr<uint8[]> buffer;
+ DEVMODE* dev_mode = NULL;
+ LONG buffer_size = DocumentProperties(NULL, printer, device_name_wide,
+ NULL, NULL, 0);
+ if (buffer_size > 0) {
+ buffer.reset(new uint8[buffer_size]);
+ memset(buffer.get(), 0, buffer_size);
+ if (DocumentProperties(NULL, printer, device_name_wide,
+ reinterpret_cast<PDEVMODE>(buffer.get()), NULL,
+ DM_OUT_BUFFER) == IDOK) {
+ dev_mode = reinterpret_cast<PDEVMODE>(buffer.get());
+ }
+ }
+ if (dev_mode == NULL) {
+ buffer.reset();
+ return OnError();
+ }
+
+ if (color == GRAY)
+ dev_mode->dmColor = DMCOLOR_MONOCHROME;
+ else
+ dev_mode->dmColor = DMCOLOR_COLOR;
+
+ dev_mode->dmCopies = std::max(copies, 1);
+ if (dev_mode->dmCopies > 1) // do not change collate unless multiple copies
+ dev_mode->dmCollate = collate ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
+ switch (duplex_mode) {
+ case LONG_EDGE:
+ dev_mode->dmDuplex = DMDUP_VERTICAL;
+ break;
+ case SHORT_EDGE:
+ dev_mode->dmDuplex = DMDUP_HORIZONTAL;
+ break;
+ case SIMPLEX:
+ dev_mode->dmDuplex = DMDUP_SIMPLEX;
+ break;
+ default: // UNKNOWN_DUPLEX_MODE
+ break;
+ }
+ dev_mode->dmOrientation = landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
+
+ // Update data using DocumentProperties.
+ if (DocumentProperties(NULL, printer, device_name_wide, dev_mode, dev_mode,
+ DM_IN_BUFFER | DM_OUT_BUFFER) != IDOK) {
+ return OnError();
+ }
+
+ // Set printer then refresh printer settings.
+ if (!AllocateContext(device_name, dev_mode, &context_)) {
+ return OnError();
+ }
+ PrintSettingsInitializerWin::InitPrintSettings(context_, *dev_mode,
+ ranges, device_name,
+ false, &settings_);
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+
+ settings_ = settings;
+
+ // TODO(maruel): settings_.ToDEVMODE()
+ ScopedPrinterHandle printer;
+ if (!printer.OpenPrinter(settings_.device_name().c_str())) {
+ return FAILED;
+ }
+
+ Result status = OK;
+
+ if (!GetPrinterSettings(printer, settings_.device_name()))
+ status = FAILED;
+
+ if (status != OK)
+ ResetSettings();
+ return status;
+}
+
+PrintingContext::Result PrintingContextWin::NewDocument(
+ const string16& document_name) {
+ DCHECK(!in_print_job_);
+ if (!context_)
+ return OnError();
+
+ // Set the flag used by the AbortPrintJob dialog procedure.
+ abort_printing_ = false;
+
+ in_print_job_ = true;
+
+ // Register the application's AbortProc function with GDI.
+ if (SP_ERROR == SetAbortProc(context_, &AbortProc))
+ return OnError();
+
+ DCHECK(PrintBackend::SimplifyDocumentTitle(document_name) == document_name);
+ DOCINFO di = { sizeof(DOCINFO) };
+ const std::wstring& document_name_wide = UTF16ToWide(document_name);
+ di.lpszDocName = document_name_wide.c_str();
+
+ // Is there a debug dump directory specified? If so, force to print to a file.
+ base::FilePath debug_dump_path = PrintedDocument::debug_dump_path();
+ if (!debug_dump_path.empty()) {
+ // Create a filename.
+ std::wstring filename;
+ Time now(Time::Now());
+ filename = base::TimeFormatShortDateNumeric(now);
+ filename += L"_";
+ filename += base::TimeFormatTimeOfDay(now);
+ filename += L"_";
+ filename += UTF16ToWide(document_name);
+ filename += L"_";
+ filename += L"buffer.prn";
+ file_util::ReplaceIllegalCharactersInPath(&filename, '_');
+ debug_dump_path.Append(filename);
+ di.lpszOutput = debug_dump_path.value().c_str();
+ }
+
+ // No message loop running in unit tests.
+ DCHECK(!base::MessageLoop::current() ||
+ !base::MessageLoop::current()->NestableTasksAllowed());
+
+ // Begin a print job by calling the StartDoc function.
+ // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
+ // IPC. Make sure recursive task processing is disabled.
+ if (StartDoc(context_, &di) <= 0)
+ return OnError();
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(context_);
+ DCHECK(in_print_job_);
+
+ // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
+ // ::StartPage().
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
+ // ::EndPage().
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+ DCHECK(context_);
+
+ // Inform the driver that document has ended.
+ if (EndDoc(context_) <= 0)
+ return OnError();
+
+ ResetSettings();
+ return OK;
+}
+
+void PrintingContextWin::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+ if (context_)
+ CancelDC(context_);
+ if (dialog_box_) {
+ DestroyWindow(dialog_box_);
+ dialog_box_dismissed_ = true;
+ }
+}
+
+void PrintingContextWin::ReleaseContext() {
+ if (context_) {
+ DeleteDC(context_);
+ context_ = NULL;
+ }
+}
+
+gfx::NativeDrawingContext PrintingContextWin::context() const {
+ return context_;
+}
+
+// static
+BOOL PrintingContextWin::AbortProc(HDC hdc, int nCode) {
+ if (nCode) {
+ // TODO(maruel): Need a way to find the right instance to set. Should
+ // leverage PrintJobManager here?
+ // abort_printing_ = true;
+ }
+ return true;
+}
+
+bool PrintingContextWin::InitializeSettings(const DEVMODE& dev_mode,
+ const std::wstring& new_device_name,
+ const PRINTPAGERANGE* ranges,
+ int number_ranges,
+ bool selection_only) {
+ skia::InitializeDC(context_);
+ DCHECK(GetDeviceCaps(context_, CLIPCAPS));
+ DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB);
+ DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64);
+ // Some printers don't advertise these.
+ // DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_SCALING);
+ // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_CONST_ALPHA);
+ // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
+
+ // StretchDIBits() support is needed for printing.
+ if (!(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB) ||
+ !(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64)) {
+ NOTREACHED();
+ ResetSettings();
+ return false;
+ }
+
+ DCHECK(!in_print_job_);
+ DCHECK(context_);
+ PageRanges ranges_vector;
+ if (!selection_only) {
+ // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
+ ranges_vector.reserve(number_ranges);
+ for (int i = 0; i < number_ranges; ++i) {
+ PageRange range;
+ // Transfer from 1-based to 0-based.
+ range.from = ranges[i].nFromPage - 1;
+ range.to = ranges[i].nToPage - 1;
+ ranges_vector.push_back(range);
+ }
+ }
+
+ PrintSettingsInitializerWin::InitPrintSettings(context_,
+ dev_mode,
+ ranges_vector,
+ new_device_name,
+ selection_only,
+ &settings_);
+
+ return true;
+}
+
+bool PrintingContextWin::GetPrinterSettings(HANDLE printer,
+ const std::wstring& device_name) {
+ DCHECK(!in_print_job_);
+
+ UserDefaultDevMode user_settings;
+
+ if (!user_settings.Init(printer) ||
+ !AllocateContext(device_name, user_settings.get(), &context_)) {
+ ResetSettings();
+ return false;
+ }
+
+ return InitializeSettings(*user_settings.get(), device_name, NULL, 0, false);
+}
+
+// static
+bool PrintingContextWin::AllocateContext(const std::wstring& device_name,
+ const DEVMODE* dev_mode,
+ gfx::NativeDrawingContext* context) {
+ *context = CreateDC(L"WINSPOOL", device_name.c_str(), NULL, dev_mode);
+ DCHECK(*context);
+ return *context != NULL;
+}
+
+PrintingContext::Result PrintingContextWin::ParseDialogResultEx(
+ const PRINTDLGEX& dialog_options) {
+ // If the user clicked OK or Apply then Cancel, but not only Cancel.
+ if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
+ // Start fresh.
+ ResetSettings();
+
+ DEVMODE* dev_mode = NULL;
+ if (dialog_options.hDevMode) {
+ dev_mode =
+ reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
+ DCHECK(dev_mode);
+ }
+
+ std::wstring device_name;
+ if (dialog_options.hDevNames) {
+ DEVNAMES* dev_names =
+ reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
+ DCHECK(dev_names);
+ if (dev_names) {
+ device_name =
+ reinterpret_cast<const wchar_t*>(
+ reinterpret_cast<const wchar_t*>(dev_names) +
+ dev_names->wDeviceOffset);
+ GlobalUnlock(dialog_options.hDevNames);
+ }
+ }
+
+ bool success = false;
+ if (dev_mode && !device_name.empty()) {
+ context_ = dialog_options.hDC;
+ PRINTPAGERANGE* page_ranges = NULL;
+ DWORD num_page_ranges = 0;
+ bool print_selection_only = false;
+ if (dialog_options.Flags & PD_PAGENUMS) {
+ page_ranges = dialog_options.lpPageRanges;
+ num_page_ranges = dialog_options.nPageRanges;
+ }
+ if (dialog_options.Flags & PD_SELECTION) {
+ print_selection_only = true;
+ }
+ success = InitializeSettings(*dev_mode,
+ device_name,
+ page_ranges,
+ num_page_ranges,
+ print_selection_only);
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ context_ = NULL;
+ }
+
+ if (dev_mode) {
+ GlobalUnlock(dialog_options.hDevMode);
+ }
+ } else {
+ if (dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ }
+ }
+
+ if (dialog_options.hDevMode != NULL)
+ GlobalFree(dialog_options.hDevMode);
+ if (dialog_options.hDevNames != NULL)
+ GlobalFree(dialog_options.hDevNames);
+
+ switch (dialog_options.dwResultAction) {
+ case PD_RESULT_PRINT:
+ return context_ ? OK : FAILED;
+ case PD_RESULT_APPLY:
+ return context_ ? CANCEL : FAILED;
+ case PD_RESULT_CANCEL:
+ return CANCEL;
+ default:
+ return FAILED;
+ }
+}
+
+PrintingContext::Result PrintingContextWin::ParseDialogResult(
+ const PRINTDLG& dialog_options) {
+ // If the user clicked OK or Apply then Cancel, but not only Cancel.
+ // Start fresh.
+ ResetSettings();
+
+ DEVMODE* dev_mode = NULL;
+ if (dialog_options.hDevMode) {
+ dev_mode =
+ reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
+ DCHECK(dev_mode);
+ }
+
+ std::wstring device_name;
+ if (dialog_options.hDevNames) {
+ DEVNAMES* dev_names =
+ reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
+ DCHECK(dev_names);
+ if (dev_names) {
+ device_name =
+ reinterpret_cast<const wchar_t*>(
+ reinterpret_cast<const wchar_t*>(dev_names) +
+ dev_names->wDeviceOffset);
+ GlobalUnlock(dialog_options.hDevNames);
+ }
+ }
+
+ bool success = false;
+ if (dev_mode && !device_name.empty()) {
+ context_ = dialog_options.hDC;
+ success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ context_ = NULL;
+ }
+
+ if (dev_mode) {
+ GlobalUnlock(dialog_options.hDevMode);
+ }
+
+ if (dialog_options.hDevMode != NULL)
+ GlobalFree(dialog_options.hDevMode);
+ if (dialog_options.hDevNames != NULL)
+ GlobalFree(dialog_options.hDevNames);
+
+ return context_ ? OK : FAILED;
+}
+
+} // namespace printing