blob: 522da3d687913fa37836477d81bdc33c9135669e [file] [log] [blame]
[email protected]55e8e9f2012-03-02 22:50:541// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]51e8d9352010-10-06 22:21:175#include "printing/printing_context_win.h"
initial.commit09911bf2008-07-26 23:55:296
7#include <winspool.h>
8
[email protected]19f09a62011-05-04 23:37:479#include <algorithm>
10
[email protected]d0767cb542009-10-08 17:38:3011#include "base/i18n/file_util_icu.h"
[email protected]260d1572009-10-09 18:50:3512#include "base/i18n/time_formatting.h"
[email protected]dcccb942009-02-01 18:23:0013#include "base/message_loop.h"
[email protected]e9c7c372012-08-13 16:36:0414#include "base/metrics/histogram.h"
[email protected]dcccb942009-02-01 18:23:0015#include "base/time.h"
[email protected]d9d42992010-09-13 19:39:1916#include "base/utf_string_conversions.h"
[email protected]89f5aa8c2011-03-21 20:58:4417#include "base/values.h"
[email protected]06186a3c2012-07-12 18:38:3018#include "base/win/metro.h"
[email protected]55e8e9f2012-03-02 22:50:5419#include "printing/backend/win_helper.h"
[email protected]c97e5e82011-04-05 18:50:2320#include "printing/print_job_constants.h"
[email protected]4993f342010-10-26 17:57:5221#include "printing/print_settings_initializer_win.h"
[email protected]8ff1d422009-07-07 21:31:3922#include "printing/printed_document.h"
[email protected]e5636a52011-09-28 21:44:4223#include "printing/units.h"
[email protected]62f2e802011-05-26 14:28:3524#include "skia/ext/platform_device.h"
initial.commit09911bf2008-07-26 23:55:2925
[email protected]e1acf6f2008-10-27 20:43:3326using base::Time;
27
[email protected]bd5baaf2011-04-21 20:48:3328namespace {
29
[email protected]e5636a52011-09-28 21:44:4230// Constants for setting default PDF settings.
31const int kPDFDpi = 300; // 300 dpi
32// LETTER: 8.5 x 11 inches
33const int kPDFLetterWidth = 8.5 * kPDFDpi;
34const int kPDFLetterHeight = 11 * kPDFDpi;
35// LEGAL: 8.5 x 14 inches
36const int kPDFLegalWidth = 8.5 * kPDFDpi;
37const int kPDFLegalHeight = 14 * kPDFDpi;
38// A4: 8.27 x 11.69 inches
39const int kPDFA4Width = 8.27 * kPDFDpi;
40const int kPDFA4Height = 11.69 * kPDFDpi;
41// A3: 11.69 x 16.54 inches
42const int kPDFA3Width = 11.69 * kPDFDpi;
43const int kPDFA3Height = 16.54 * kPDFDpi;
44
[email protected]bd5baaf2011-04-21 20:48:3345// Retrieves the printer's PRINTER_INFO_* structure.
46// Output |level| can be 9 (user-default), 8 (admin-default), or 2
47// (printer-default).
48// |devmode| is a pointer points to the start of DEVMODE structure in
49// |buffer|.
50bool GetPrinterInfo(HANDLE printer,
51 const std::wstring &device_name,
52 int* level,
53 scoped_array<uint8>* buffer,
54 DEVMODE** dev_mode) {
55 DCHECK(buffer);
56
57 // A PRINTER_INFO_9 structure specifying the per-user default printer
58 // settings.
59 printing::PrintingContextWin::GetPrinterHelper(printer, 9, buffer);
60 if (buffer->get()) {
61 PRINTER_INFO_9* info_9 = reinterpret_cast<PRINTER_INFO_9*>(buffer->get());
62 if (info_9->pDevMode != NULL) {
63 *level = 9;
64 *dev_mode = info_9->pDevMode;
65 return true;
66 }
67 buffer->reset();
68 }
69
70 // A PRINTER_INFO_8 structure specifying the global default printer settings.
71 printing::PrintingContextWin::GetPrinterHelper(printer, 8, buffer);
72 if (buffer->get()) {
73 PRINTER_INFO_8* info_8 = reinterpret_cast<PRINTER_INFO_8*>(buffer->get());
74 if (info_8->pDevMode != NULL) {
75 *level = 8;
76 *dev_mode = info_8->pDevMode;
77 return true;
78 }
79 buffer->reset();
80 }
81
82 // A PRINTER_INFO_2 structure specifying the driver's default printer
83 // settings.
84 printing::PrintingContextWin::GetPrinterHelper(printer, 2, buffer);
85 if (buffer->get()) {
86 PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(buffer->get());
87 if (info_2->pDevMode != NULL) {
88 *level = 2;
89 *dev_mode = info_2->pDevMode;
90 return true;
91 }
92 buffer->reset();
93 }
94
95 return false;
96}
97
98} // anonymous namespace
99
initial.commit09911bf2008-07-26 23:55:29100namespace printing {
101
[email protected]51e8d9352010-10-06 22:21:17102class PrintingContextWin::CallbackHandler : public IPrintDialogCallback,
103 public IObjectWithSite {
initial.commit09911bf2008-07-26 23:55:29104 public:
[email protected]51e8d9352010-10-06 22:21:17105 CallbackHandler(PrintingContextWin& owner, HWND owner_hwnd)
initial.commit09911bf2008-07-26 23:55:29106 : owner_(owner),
107 owner_hwnd_(owner_hwnd),
108 services_(NULL) {
109 }
110
111 ~CallbackHandler() {
112 if (services_)
113 services_->Release();
114 }
115
116 IUnknown* ToIUnknown() {
117 return static_cast<IUnknown*>(static_cast<IPrintDialogCallback*>(this));
118 }
119
120 // IUnknown
121 virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) {
122 if (riid == IID_IUnknown) {
123 *object = ToIUnknown();
124 } else if (riid == IID_IPrintDialogCallback) {
125 *object = static_cast<IPrintDialogCallback*>(this);
126 } else if (riid == IID_IObjectWithSite) {
127 *object = static_cast<IObjectWithSite*>(this);
128 } else {
129 return E_NOINTERFACE;
130 }
131 return S_OK;
132 }
133
134 // No real ref counting.
135 virtual ULONG WINAPI AddRef() {
136 return 1;
137 }
138 virtual ULONG WINAPI Release() {
139 return 1;
140 }
141
142 // IPrintDialogCallback methods
143 virtual HRESULT WINAPI InitDone() {
144 return S_OK;
145 }
146
147 virtual HRESULT WINAPI SelectionChange() {
148 if (services_) {
149 // TODO(maruel): Get the devmode for the new printer with
150 // services_->GetCurrentDevMode(&devmode, &size), send that information
151 // back to our client and continue. The client needs to recalculate the
152 // number of rendered pages and send back this information here.
153 }
154 return S_OK;
155 }
156
157 virtual HRESULT WINAPI HandleMessage(HWND dialog,
158 UINT message,
159 WPARAM wparam,
160 LPARAM lparam,
161 LRESULT* result) {
162 // Cheap way to retrieve the window handle.
163 if (!owner_.dialog_box_) {
164 // The handle we receive is the one of the groupbox in the General tab. We
165 // need to get the grand-father to get the dialog box handle.
166 owner_.dialog_box_ = GetAncestor(dialog, GA_ROOT);
167 // Trick to enable the owner window. This can cause issues with navigation
168 // events so it may have to be disabled if we don't fix the side-effects.
169 EnableWindow(owner_hwnd_, TRUE);
170 }
171 return S_FALSE;
172 }
173
174 virtual HRESULT WINAPI SetSite(IUnknown* site) {
175 if (!site) {
176 DCHECK(services_);
177 services_->Release();
178 services_ = NULL;
179 // The dialog box is destroying, PrintJob::Worker don't need the handle
180 // anymore.
181 owner_.dialog_box_ = NULL;
182 } else {
183 DCHECK(services_ == NULL);
184 HRESULT hr = site->QueryInterface(IID_IPrintDialogServices,
185 reinterpret_cast<void**>(&services_));
186 DCHECK(SUCCEEDED(hr));
187 }
188 return S_OK;
189 }
190
191 virtual HRESULT WINAPI GetSite(REFIID riid, void** site) {
192 return E_NOTIMPL;
193 }
194
195 private:
[email protected]51e8d9352010-10-06 22:21:17196 PrintingContextWin& owner_;
initial.commit09911bf2008-07-26 23:55:29197 HWND owner_hwnd_;
198 IPrintDialogServices* services_;
199
[email protected]5930cb62009-12-08 02:04:22200 DISALLOW_COPY_AND_ASSIGN(CallbackHandler);
initial.commit09911bf2008-07-26 23:55:29201};
202
[email protected]51e8d9352010-10-06 22:21:17203// static
[email protected]ee5f36e42010-12-03 22:40:37204PrintingContext* PrintingContext::Create(const std::string& app_locale) {
205 return static_cast<PrintingContext*>(new PrintingContextWin(app_locale));
[email protected]51e8d9352010-10-06 22:21:17206}
207
[email protected]ee5f36e42010-12-03 22:40:37208PrintingContextWin::PrintingContextWin(const std::string& app_locale)
209 : PrintingContext(app_locale),
[email protected]51e8d9352010-10-06 22:21:17210 context_(NULL),
initial.commit09911bf2008-07-26 23:55:29211 dialog_box_(NULL),
[email protected]d8254622010-08-13 19:15:46212 print_dialog_func_(&PrintDlgEx) {
initial.commit09911bf2008-07-26 23:55:29213}
214
[email protected]51e8d9352010-10-06 22:21:17215PrintingContextWin::~PrintingContextWin() {
216 ReleaseContext();
initial.commit09911bf2008-07-26 23:55:29217}
218
[email protected]abe48112011-11-19 01:58:38219void PrintingContextWin::AskUserForSettings(
220 gfx::NativeView view, int max_pages, bool has_selection,
221 const PrintSettingsCallback& callback) {
[email protected]94fbaa42011-09-07 20:09:54222#if !defined(USE_AURA)
initial.commit09911bf2008-07-26 23:55:29223 DCHECK(!in_print_job_);
[email protected]06186a3c2012-07-12 18:38:30224
225 if (base::win::IsMetroProcess()) {
226 // The system dialog can not be opened while running in Metro.
227 // But we can programatically launch the Metro print device charm though.
228 HMODULE metro_module = base::win::GetMetroModule();
229 if (metro_module != NULL) {
230 typedef void (*MetroShowPrintUI)();
231 MetroShowPrintUI metro_show_print_ui =
232 reinterpret_cast<MetroShowPrintUI>(
233 ::GetProcAddress(metro_module, "MetroShowPrintUI"));
[email protected]e9c7c372012-08-13 16:36:04234 if (metro_show_print_ui) {
235 // TODO(mad): Remove this once we can send user metrics from the metro
236 // driver. crbug.com/142330
237 UMA_HISTOGRAM_ENUMERATION("Metro.Print", 1, 2);
[email protected]06186a3c2012-07-12 18:38:30238 metro_show_print_ui();
[email protected]e9c7c372012-08-13 16:36:04239 }
[email protected]06186a3c2012-07-12 18:38:30240 }
241 return callback.Run(CANCEL);
242 }
initial.commit09911bf2008-07-26 23:55:29243 dialog_box_dismissed_ = false;
[email protected]fc7904622010-05-12 19:26:40244
245 HWND window;
246 if (!view || !IsWindow(view)) {
247 // TODO(maruel): bug 1214347 Get the right browser window instead.
248 window = GetDesktopWindow();
249 } else {
250 window = GetAncestor(view, GA_ROOTOWNER);
251 }
252 DCHECK(window);
253
initial.commit09911bf2008-07-26 23:55:29254 // Show the OS-dependent dialog box.
255 // If the user press
256 // - OK, the settings are reset and reinitialized with the new settings. OK is
257 // returned.
258 // - Apply then Cancel, the settings are reset and reinitialized with the new
259 // settings. CANCEL is returned.
260 // - Cancel, the settings are not changed, the previous setting, if it was
261 // initialized before, are kept. CANCEL is returned.
262 // On failure, the settings are reset and FAILED is returned.
263 PRINTDLGEX dialog_options = { sizeof(PRINTDLGEX) };
264 dialog_options.hwndOwner = window;
[email protected]c8ad40c2009-06-08 17:05:21265 // Disable options we don't support currently.
initial.commit09911bf2008-07-26 23:55:29266 // TODO(maruel): Reuse the previously loaded settings!
267 dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
[email protected]c8ad40c2009-06-08 17:05:21268 PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
269 if (!has_selection)
270 dialog_options.Flags |= PD_NOSELECTION;
271
initial.commit09911bf2008-07-26 23:55:29272 PRINTPAGERANGE ranges[32];
273 dialog_options.nStartPage = START_PAGE_GENERAL;
274 if (max_pages) {
275 // Default initialize to print all the pages.
276 memset(ranges, 0, sizeof(ranges));
277 ranges[0].nFromPage = 1;
278 ranges[0].nToPage = max_pages;
279 dialog_options.nPageRanges = 1;
280 dialog_options.nMaxPageRanges = arraysize(ranges);
[email protected]3a0e4a32009-06-09 19:07:05281 dialog_options.nMinPage = 1;
initial.commit09911bf2008-07-26 23:55:29282 dialog_options.nMaxPage = max_pages;
283 dialog_options.lpPageRanges = ranges;
284 } else {
285 // No need to bother, we don't know how many pages are available.
286 dialog_options.Flags |= PD_NOPAGENUMS;
287 }
288
[email protected]128bec3c2011-01-20 22:57:35289 if ((*print_dialog_func_)(&dialog_options) != S_OK) {
290 ResetSettings();
[email protected]abe48112011-11-19 01:58:38291 callback.Run(FAILED);
initial.commit09911bf2008-07-26 23:55:29292 }
[email protected]128bec3c2011-01-20 22:57:35293
initial.commit09911bf2008-07-26 23:55:29294 // TODO(maruel): Support PD_PRINTTOFILE.
[email protected]abe48112011-11-19 01:58:38295 callback.Run(ParseDialogResultEx(dialog_options));
[email protected]94fbaa42011-09-07 20:09:54296#endif
initial.commit09911bf2008-07-26 23:55:29297}
298
[email protected]51e8d9352010-10-06 22:21:17299PrintingContext::Result PrintingContextWin::UseDefaultSettings() {
initial.commit09911bf2008-07-26 23:55:29300 DCHECK(!in_print_job_);
301
302 PRINTDLG dialog_options = { sizeof(PRINTDLG) };
303 dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
[email protected]ff3ccc22011-04-18 21:35:48304 if (PrintDlg(&dialog_options))
305 return ParseDialogResult(dialog_options);
306
307 // No default printer configured, do we have any printers at all?
308 DWORD bytes_needed = 0;
309 DWORD count_returned = 0;
310 (void)::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
311 NULL, 2, NULL, 0, &bytes_needed, &count_returned);
312 if (bytes_needed) {
313 DCHECK(bytes_needed >= count_returned * sizeof(PRINTER_INFO_2));
314 scoped_array<BYTE> printer_info_buffer(new BYTE[bytes_needed]);
315 BOOL ret = ::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
316 NULL, 2, printer_info_buffer.get(),
317 bytes_needed, &bytes_needed,
318 &count_returned);
319 if (ret && count_returned) { // have printers
320 // Open the first successfully found printer.
[email protected]a5e0ef572011-04-21 18:38:54321 for (DWORD count = 0; count < count_returned; ++count) {
322 PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(
[email protected]ff3ccc22011-04-18 21:35:48323 printer_info_buffer.get() + count * sizeof(PRINTER_INFO_2));
324 std::wstring printer_name = info_2->pPrinterName;
325 if (info_2->pDevMode == NULL || printer_name.length() == 0)
326 continue;
327 if (!AllocateContext(printer_name, info_2->pDevMode, &context_))
328 break;
329 if (InitializeSettings(*info_2->pDevMode, printer_name,
[email protected]a5e0ef572011-04-21 18:38:54330 NULL, 0, false)) {
[email protected]ff3ccc22011-04-18 21:35:48331 break;
[email protected]ff3ccc22011-04-18 21:35:48332 }
[email protected]a5e0ef572011-04-21 18:38:54333 ReleaseContext();
[email protected]ff3ccc22011-04-18 21:35:48334 }
335 if (context_)
336 return OK;
337 }
initial.commit09911bf2008-07-26 23:55:29338 }
[email protected]ff3ccc22011-04-18 21:35:48339
340 ResetSettings();
341 return FAILED;
initial.commit09911bf2008-07-26 23:55:29342}
343
[email protected]55b23a02011-08-17 23:09:36344PrintingContext::Result PrintingContextWin::UpdatePrinterSettings(
[email protected]bd5baaf2011-04-21 20:48:33345 const DictionaryValue& job_settings,
346 const PageRanges& ranges) {
[email protected]7868ecab2011-03-05 00:12:53347 DCHECK(!in_print_job_);
348
[email protected]19f09a62011-05-04 23:37:47349 bool collate;
[email protected]f3256b0d82011-09-04 23:36:29350 int color;
[email protected]19f09a62011-05-04 23:37:47351 bool landscape;
352 bool print_to_pdf;
[email protected]737cfb22011-10-04 18:06:29353 bool is_cloud_dialog;
[email protected]19f09a62011-05-04 23:37:47354 int copies;
355 int duplex_mode;
[email protected]9f47bbf2011-05-06 19:03:17356 string16 device_name;
[email protected]19f09a62011-05-04 23:37:47357
358 if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
359 !job_settings.GetBoolean(kSettingCollate, &collate) ||
[email protected]f3256b0d82011-09-04 23:36:29360 !job_settings.GetInteger(kSettingColor, &color) ||
[email protected]19f09a62011-05-04 23:37:47361 !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
[email protected]bd5baaf2011-04-21 20:48:33362 !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
[email protected]19f09a62011-05-04 23:37:47363 !job_settings.GetInteger(kSettingCopies, &copies) ||
[email protected]737cfb22011-10-04 18:06:29364 !job_settings.GetString(kSettingDeviceName, &device_name) ||
365 !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
[email protected]c48bee22011-03-29 02:36:26366 return OnError();
[email protected]bd5baaf2011-04-21 20:48:33367 }
[email protected]37a401bf2011-03-31 16:12:36368
[email protected]d91db112011-10-18 20:58:51369 bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
[email protected]71dfff72011-07-23 01:27:27370
[email protected]737cfb22011-10-04 18:06:29371 if (print_to_pdf || print_to_cloud || is_cloud_dialog) {
[email protected]e5636a52011-09-28 21:44:42372 // Default fallback to Letter size.
373 gfx::Size paper_size;
374 gfx::Rect paper_rect;
375 paper_size.SetSize(kPDFLetterWidth, kPDFLetterHeight);
376
377 // Get settings from locale. Paper type buffer length is at most 4.
378 const int paper_type_buffer_len = 4;
379 wchar_t paper_type_buffer[paper_type_buffer_len] = {0};
380 GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE, paper_type_buffer,
381 paper_type_buffer_len);
382 if (wcslen(paper_type_buffer)) { // The call succeeded.
383 int paper_code = _wtoi(paper_type_buffer);
384 switch (paper_code) {
385 case DMPAPER_LEGAL:
386 paper_size.SetSize(kPDFLegalWidth, kPDFLegalHeight);
387 break;
388 case DMPAPER_A4:
389 paper_size.SetSize(kPDFA4Width, kPDFA4Height);
390 break;
391 case DMPAPER_A3:
392 paper_size.SetSize(kPDFA3Width, kPDFA3Height);
393 break;
394 default: // DMPAPER_LETTER is used for default fallback.
395 break;
396 }
397 }
398 paper_rect.SetRect(0, 0, paper_size.width(), paper_size.height());
399 settings_.SetPrinterPrintableArea(paper_size, paper_rect, kPDFDpi);
400 settings_.set_dpi(kPDFDpi);
[email protected]19f09a62011-05-04 23:37:47401 settings_.SetOrientation(landscape);
[email protected]30aaa81a2011-05-03 21:08:39402 settings_.ranges = ranges;
403 return OK;
404 }
405
[email protected]55e8e9f2012-03-02 22:50:54406 ScopedPrinterHandle printer;
[email protected]9f47bbf2011-05-06 19:03:17407 LPWSTR device_name_wide = const_cast<wchar_t*>(device_name.c_str());
[email protected]55e8e9f2012-03-02 22:50:54408 if (!OpenPrinter(device_name_wide, printer.Receive(), NULL))
[email protected]bd5baaf2011-04-21 20:48:33409 return OnError();
[email protected]afbdbf112011-03-28 22:09:37410
[email protected]30aaa81a2011-05-03 21:08:39411 // Make printer changes local to Chrome.
412 // See MSDN documentation regarding DocumentProperties.
[email protected]bd5baaf2011-04-21 20:48:33413 scoped_array<uint8> buffer;
[email protected]bd5baaf2011-04-21 20:48:33414 DEVMODE* dev_mode = NULL;
[email protected]9f47bbf2011-05-06 19:03:17415 LONG buffer_size = DocumentProperties(NULL, printer, device_name_wide,
[email protected]30aaa81a2011-05-03 21:08:39416 NULL, NULL, 0);
[email protected]bcf63422011-06-14 18:57:39417 if (buffer_size > 0) {
[email protected]30aaa81a2011-05-03 21:08:39418 buffer.reset(new uint8[buffer_size]);
419 memset(buffer.get(), 0, buffer_size);
[email protected]9f47bbf2011-05-06 19:03:17420 if (DocumentProperties(NULL, printer, device_name_wide,
[email protected]30aaa81a2011-05-03 21:08:39421 reinterpret_cast<PDEVMODE>(buffer.get()), NULL,
422 DM_OUT_BUFFER) == IDOK) {
423 dev_mode = reinterpret_cast<PDEVMODE>(buffer.get());
424 }
425 }
426 if (dev_mode == NULL) {
427 buffer.reset();
[email protected]bd5baaf2011-04-21 20:48:33428 return OnError();
429 }
[email protected]7868ecab2011-03-05 00:12:53430
[email protected]d91db112011-10-18 20:58:51431 if (color == GRAY)
[email protected]f3256b0d82011-09-04 23:36:29432 dev_mode->dmColor = DMCOLOR_MONOCHROME;
433 else
434 dev_mode->dmColor = DMCOLOR_COLOR;
435
[email protected]bd5baaf2011-04-21 20:48:33436 dev_mode->dmCopies = std::max(copies, 1);
437 if (dev_mode->dmCopies > 1) // do not change collate unless multiple copies
[email protected]19f09a62011-05-04 23:37:47438 dev_mode->dmCollate = collate ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
[email protected]bd5baaf2011-04-21 20:48:33439 switch (duplex_mode) {
440 case LONG_EDGE:
441 dev_mode->dmDuplex = DMDUP_VERTICAL;
442 break;
443 case SHORT_EDGE:
444 dev_mode->dmDuplex = DMDUP_HORIZONTAL;
445 break;
[email protected]dc8373d2011-09-08 21:32:35446 case SIMPLEX:
[email protected]bd5baaf2011-04-21 20:48:33447 dev_mode->dmDuplex = DMDUP_SIMPLEX;
448 break;
[email protected]dc8373d2011-09-08 21:32:35449 default: // UNKNOWN_DUPLEX_MODE
450 break;
[email protected]bd5baaf2011-04-21 20:48:33451 }
[email protected]19f09a62011-05-04 23:37:47452 dev_mode->dmOrientation = landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
[email protected]bd5baaf2011-04-21 20:48:33453
[email protected]30aaa81a2011-05-03 21:08:39454 // Update data using DocumentProperties.
[email protected]9f47bbf2011-05-06 19:03:17455 if (DocumentProperties(NULL, printer, device_name_wide, dev_mode, dev_mode,
[email protected]30aaa81a2011-05-03 21:08:39456 DM_IN_BUFFER | DM_OUT_BUFFER) != IDOK) {
[email protected]30aaa81a2011-05-03 21:08:39457 return OnError();
458 }
459
[email protected]bd5baaf2011-04-21 20:48:33460 // Set printer then refresh printer settings.
[email protected]9f47bbf2011-05-06 19:03:17461 if (!AllocateContext(device_name, dev_mode, &context_)) {
[email protected]bd5baaf2011-04-21 20:48:33462 return OnError();
463 }
464 PrintSettingsInitializerWin::InitPrintSettings(context_, *dev_mode,
[email protected]9f47bbf2011-05-06 19:03:17465 ranges, device_name,
[email protected]bd5baaf2011-04-21 20:48:33466 false, &settings_);
[email protected]afbdbf112011-03-28 22:09:37467 return OK;
[email protected]7868ecab2011-03-05 00:12:53468}
469
[email protected]51e8d9352010-10-06 22:21:17470PrintingContext::Result PrintingContextWin::InitWithSettings(
initial.commit09911bf2008-07-26 23:55:29471 const PrintSettings& settings) {
472 DCHECK(!in_print_job_);
[email protected]4993f342010-10-26 17:57:52473
initial.commit09911bf2008-07-26 23:55:29474 settings_ = settings;
[email protected]4993f342010-10-26 17:57:52475
476 // TODO(maruel): settings_.ToDEVMODE()
[email protected]55e8e9f2012-03-02 22:50:54477 ScopedPrinterHandle printer;
initial.commit09911bf2008-07-26 23:55:29478 if (!OpenPrinter(const_cast<wchar_t*>(settings_.device_name().c_str()),
[email protected]55e8e9f2012-03-02 22:50:54479 printer.Receive(), NULL))
initial.commit09911bf2008-07-26 23:55:29480 return FAILED;
481
482 Result status = OK;
483
484 if (!GetPrinterSettings(printer, settings_.device_name()))
485 status = FAILED;
486
initial.commit09911bf2008-07-26 23:55:29487 if (status != OK)
488 ResetSettings();
489 return status;
490}
491
[email protected]51e8d9352010-10-06 22:21:17492PrintingContext::Result PrintingContextWin::NewDocument(
[email protected]d9d42992010-09-13 19:39:19493 const string16& document_name) {
initial.commit09911bf2008-07-26 23:55:29494 DCHECK(!in_print_job_);
[email protected]b75dca82009-10-13 18:46:21495 if (!context_)
[email protected]c8ad40c2009-06-08 17:05:21496 return OnError();
initial.commit09911bf2008-07-26 23:55:29497
498 // Set the flag used by the AbortPrintJob dialog procedure.
499 abort_printing_ = false;
500
501 in_print_job_ = true;
502
503 // Register the application's AbortProc function with GDI.
[email protected]b75dca82009-10-13 18:46:21504 if (SP_ERROR == SetAbortProc(context_, &AbortProc))
[email protected]c8ad40c2009-06-08 17:05:21505 return OnError();
initial.commit09911bf2008-07-26 23:55:29506
507 DOCINFO di = { sizeof(DOCINFO) };
[email protected]ea6f72a97c2010-09-28 00:34:47508 const std::wstring& document_name_wide = UTF16ToWide(document_name);
509 di.lpszDocName = document_name_wide.c_str();
initial.commit09911bf2008-07-26 23:55:29510
511 // Is there a debug dump directory specified? If so, force to print to a file.
[email protected]d9d42992010-09-13 19:39:19512 FilePath debug_dump_path = PrintedDocument::debug_dump_path();
initial.commit09911bf2008-07-26 23:55:29513 if (!debug_dump_path.empty()) {
514 // Create a filename.
515 std::wstring filename;
516 Time now(Time::Now());
[email protected]5cca3a52008-08-19 22:35:29517 filename = base::TimeFormatShortDateNumeric(now);
initial.commit09911bf2008-07-26 23:55:29518 filename += L"_";
[email protected]5cca3a52008-08-19 22:35:29519 filename += base::TimeFormatTimeOfDay(now);
initial.commit09911bf2008-07-26 23:55:29520 filename += L"_";
[email protected]d9d42992010-09-13 19:39:19521 filename += UTF16ToWide(document_name);
initial.commit09911bf2008-07-26 23:55:29522 filename += L"_";
523 filename += L"buffer.prn";
[email protected]de2943352009-10-22 23:06:12524 file_util::ReplaceIllegalCharactersInPath(&filename, '_');
[email protected]d9d42992010-09-13 19:39:19525 debug_dump_path.Append(filename);
526 di.lpszOutput = debug_dump_path.value().c_str();
initial.commit09911bf2008-07-26 23:55:29527 }
528
[email protected]daee4972009-07-09 14:28:24529 // No message loop running in unit tests.
530 DCHECK(!MessageLoop::current() ? true :
531 !MessageLoop::current()->NestableTasksAllowed());
532
initial.commit09911bf2008-07-26 23:55:29533 // Begin a print job by calling the StartDoc function.
534 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
535 // IPC. Make sure recursive task processing is disabled.
[email protected]b75dca82009-10-13 18:46:21536 if (StartDoc(context_, &di) <= 0)
[email protected]c8ad40c2009-06-08 17:05:21537 return OnError();
initial.commit09911bf2008-07-26 23:55:29538
initial.commit09911bf2008-07-26 23:55:29539 return OK;
540}
541
[email protected]51e8d9352010-10-06 22:21:17542PrintingContext::Result PrintingContextWin::NewPage() {
initial.commit09911bf2008-07-26 23:55:29543 if (abort_printing_)
544 return CANCEL;
[email protected]3b52c982010-09-27 20:40:36545 DCHECK(context_);
initial.commit09911bf2008-07-26 23:55:29546 DCHECK(in_print_job_);
547
[email protected]b10c54d2011-03-16 18:22:35548 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
549 // ::StartPage().
initial.commit09911bf2008-07-26 23:55:29550
initial.commit09911bf2008-07-26 23:55:29551 return OK;
552}
553
[email protected]51e8d9352010-10-06 22:21:17554PrintingContext::Result PrintingContextWin::PageDone() {
initial.commit09911bf2008-07-26 23:55:29555 if (abort_printing_)
556 return CANCEL;
557 DCHECK(in_print_job_);
558
[email protected]b10c54d2011-03-16 18:22:35559 // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
560 // ::EndPage().
561
initial.commit09911bf2008-07-26 23:55:29562 return OK;
563}
564
[email protected]51e8d9352010-10-06 22:21:17565PrintingContext::Result PrintingContextWin::DocumentDone() {
initial.commit09911bf2008-07-26 23:55:29566 if (abort_printing_)
567 return CANCEL;
568 DCHECK(in_print_job_);
[email protected]60745412010-09-27 23:46:07569 DCHECK(context_);
initial.commit09911bf2008-07-26 23:55:29570
571 // Inform the driver that document has ended.
[email protected]b75dca82009-10-13 18:46:21572 if (EndDoc(context_) <= 0)
[email protected]c8ad40c2009-06-08 17:05:21573 return OnError();
initial.commit09911bf2008-07-26 23:55:29574
575 ResetSettings();
576 return OK;
577}
578
[email protected]51e8d9352010-10-06 22:21:17579void PrintingContextWin::Cancel() {
initial.commit09911bf2008-07-26 23:55:29580 abort_printing_ = true;
581 in_print_job_ = false;
[email protected]b75dca82009-10-13 18:46:21582 if (context_)
583 CancelDC(context_);
initial.commit09911bf2008-07-26 23:55:29584 if (dialog_box_) {
585 DestroyWindow(dialog_box_);
586 dialog_box_dismissed_ = true;
587 }
588}
589
[email protected]51e8d9352010-10-06 22:21:17590void PrintingContextWin::ReleaseContext() {
591 if (context_) {
592 DeleteDC(context_);
593 context_ = NULL;
594 }
595}
596
597gfx::NativeDrawingContext PrintingContextWin::context() const {
598 return context_;
initial.commit09911bf2008-07-26 23:55:29599}
600
601// static
[email protected]51e8d9352010-10-06 22:21:17602BOOL PrintingContextWin::AbortProc(HDC hdc, int nCode) {
initial.commit09911bf2008-07-26 23:55:29603 if (nCode) {
604 // TODO(maruel): Need a way to find the right instance to set. Should
605 // leverage PrintJobManager here?
606 // abort_printing_ = true;
607 }
608 return true;
609}
610
[email protected]51e8d9352010-10-06 22:21:17611bool PrintingContextWin::InitializeSettings(const DEVMODE& dev_mode,
612 const std::wstring& new_device_name,
613 const PRINTPAGERANGE* ranges,
614 int number_ranges,
615 bool selection_only) {
[email protected]62f2e802011-05-26 14:28:35616 skia::InitializeDC(context_);
[email protected]b75dca82009-10-13 18:46:21617 DCHECK(GetDeviceCaps(context_, CLIPCAPS));
618 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB);
619 DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64);
initial.commit09911bf2008-07-26 23:55:29620 // Some printers don't advertise these.
[email protected]b75dca82009-10-13 18:46:21621 // DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_SCALING);
622 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_CONST_ALPHA);
623 // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
initial.commit09911bf2008-07-26 23:55:29624
625 // StretchDIBits() support is needed for printing.
[email protected]b75dca82009-10-13 18:46:21626 if (!(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB) ||
627 !(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64)) {
initial.commit09911bf2008-07-26 23:55:29628 NOTREACHED();
629 ResetSettings();
630 return false;
631 }
632
633 DCHECK(!in_print_job_);
[email protected]b75dca82009-10-13 18:46:21634 DCHECK(context_);
initial.commit09911bf2008-07-26 23:55:29635 PageRanges ranges_vector;
[email protected]82270452009-06-19 15:58:01636 if (!selection_only) {
637 // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
638 ranges_vector.reserve(number_ranges);
639 for (int i = 0; i < number_ranges; ++i) {
640 PageRange range;
641 // Transfer from 1-based to 0-based.
642 range.from = ranges[i].nFromPage - 1;
643 range.to = ranges[i].nToPage - 1;
644 ranges_vector.push_back(range);
645 }
initial.commit09911bf2008-07-26 23:55:29646 }
[email protected]4993f342010-10-26 17:57:52647
648 PrintSettingsInitializerWin::InitPrintSettings(context_,
649 dev_mode,
650 ranges_vector,
651 new_device_name,
652 selection_only,
653 &settings_);
654
initial.commit09911bf2008-07-26 23:55:29655 return true;
656}
657
[email protected]51e8d9352010-10-06 22:21:17658bool PrintingContextWin::GetPrinterSettings(HANDLE printer,
659 const std::wstring& device_name) {
initial.commit09911bf2008-07-26 23:55:29660 DCHECK(!in_print_job_);
661 scoped_array<uint8> buffer;
[email protected]bd5baaf2011-04-21 20:48:33662 int level = 0;
663 DEVMODE* dev_mode = NULL;
initial.commit09911bf2008-07-26 23:55:29664
[email protected]bd5baaf2011-04-21 20:48:33665 if (GetPrinterInfo(printer, device_name, &level, &buffer, &dev_mode) &&
666 AllocateContext(device_name, dev_mode, &context_)) {
667 return InitializeSettings(*dev_mode, device_name, NULL, 0, false);
initial.commit09911bf2008-07-26 23:55:29668 }
669
[email protected]bd5baaf2011-04-21 20:48:33670 buffer.reset();
initial.commit09911bf2008-07-26 23:55:29671 ResetSettings();
672 return false;
673}
674
[email protected]d8254622010-08-13 19:15:46675// static
[email protected]9f47bbf2011-05-06 19:03:17676bool PrintingContextWin::AllocateContext(const std::wstring& device_name,
[email protected]51e8d9352010-10-06 22:21:17677 const DEVMODE* dev_mode,
678 gfx::NativeDrawingContext* context) {
[email protected]9f47bbf2011-05-06 19:03:17679 *context = CreateDC(L"WINSPOOL", device_name.c_str(), NULL, dev_mode);
[email protected]d8254622010-08-13 19:15:46680 DCHECK(*context);
681 return *context != NULL;
initial.commit09911bf2008-07-26 23:55:29682}
683
[email protected]51e8d9352010-10-06 22:21:17684PrintingContext::Result PrintingContextWin::ParseDialogResultEx(
initial.commit09911bf2008-07-26 23:55:29685 const PRINTDLGEX& dialog_options) {
686 // If the user clicked OK or Apply then Cancel, but not only Cancel.
687 if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
688 // Start fresh.
689 ResetSettings();
690
691 DEVMODE* dev_mode = NULL;
692 if (dialog_options.hDevMode) {
693 dev_mode =
694 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
695 DCHECK(dev_mode);
696 }
697
698 std::wstring device_name;
699 if (dialog_options.hDevNames) {
700 DEVNAMES* dev_names =
701 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
702 DCHECK(dev_names);
703 if (dev_names) {
704 device_name =
705 reinterpret_cast<const wchar_t*>(
706 reinterpret_cast<const wchar_t*>(dev_names) +
707 dev_names->wDeviceOffset);
708 GlobalUnlock(dialog_options.hDevNames);
709 }
710 }
711
712 bool success = false;
713 if (dev_mode && !device_name.empty()) {
[email protected]b75dca82009-10-13 18:46:21714 context_ = dialog_options.hDC;
[email protected]c8ad40c2009-06-08 17:05:21715 PRINTPAGERANGE* page_ranges = NULL;
716 DWORD num_page_ranges = 0;
717 bool print_selection_only = false;
initial.commit09911bf2008-07-26 23:55:29718 if (dialog_options.Flags & PD_PAGENUMS) {
[email protected]c8ad40c2009-06-08 17:05:21719 page_ranges = dialog_options.lpPageRanges;
720 num_page_ranges = dialog_options.nPageRanges;
initial.commit09911bf2008-07-26 23:55:29721 }
[email protected]c8ad40c2009-06-08 17:05:21722 if (dialog_options.Flags & PD_SELECTION) {
723 print_selection_only = true;
724 }
725 success = InitializeSettings(*dev_mode,
726 device_name,
[email protected]d8254622010-08-13 19:15:46727 page_ranges,
728 num_page_ranges,
[email protected]c8ad40c2009-06-08 17:05:21729 print_selection_only);
initial.commit09911bf2008-07-26 23:55:29730 }
731
732 if (!success && dialog_options.hDC) {
733 DeleteDC(dialog_options.hDC);
[email protected]b75dca82009-10-13 18:46:21734 context_ = NULL;
initial.commit09911bf2008-07-26 23:55:29735 }
736
737 if (dev_mode) {
738 GlobalUnlock(dialog_options.hDevMode);
739 }
740 } else {
741 if (dialog_options.hDC) {
742 DeleteDC(dialog_options.hDC);
743 }
744 }
745
746 if (dialog_options.hDevMode != NULL)
747 GlobalFree(dialog_options.hDevMode);
748 if (dialog_options.hDevNames != NULL)
749 GlobalFree(dialog_options.hDevNames);
750
751 switch (dialog_options.dwResultAction) {
752 case PD_RESULT_PRINT:
[email protected]b75dca82009-10-13 18:46:21753 return context_ ? OK : FAILED;
initial.commit09911bf2008-07-26 23:55:29754 case PD_RESULT_APPLY:
[email protected]b75dca82009-10-13 18:46:21755 return context_ ? CANCEL : FAILED;
initial.commit09911bf2008-07-26 23:55:29756 case PD_RESULT_CANCEL:
757 return CANCEL;
758 default:
759 return FAILED;
760 }
761}
762
[email protected]51e8d9352010-10-06 22:21:17763PrintingContext::Result PrintingContextWin::ParseDialogResult(
initial.commit09911bf2008-07-26 23:55:29764 const PRINTDLG& dialog_options) {
765 // If the user clicked OK or Apply then Cancel, but not only Cancel.
766 // Start fresh.
767 ResetSettings();
768
769 DEVMODE* dev_mode = NULL;
770 if (dialog_options.hDevMode) {
771 dev_mode =
772 reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
773 DCHECK(dev_mode);
774 }
775
776 std::wstring device_name;
777 if (dialog_options.hDevNames) {
778 DEVNAMES* dev_names =
779 reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
780 DCHECK(dev_names);
781 if (dev_names) {
782 device_name =
783 reinterpret_cast<const wchar_t*>(
784 reinterpret_cast<const wchar_t*>(dev_names) +
785 dev_names->wDeviceOffset);
786 GlobalUnlock(dialog_options.hDevNames);
787 }
788 }
789
790 bool success = false;
791 if (dev_mode && !device_name.empty()) {
[email protected]b75dca82009-10-13 18:46:21792 context_ = dialog_options.hDC;
[email protected]c8ad40c2009-06-08 17:05:21793 success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
initial.commit09911bf2008-07-26 23:55:29794 }
795
796 if (!success && dialog_options.hDC) {
797 DeleteDC(dialog_options.hDC);
[email protected]b75dca82009-10-13 18:46:21798 context_ = NULL;
initial.commit09911bf2008-07-26 23:55:29799 }
800
801 if (dev_mode) {
802 GlobalUnlock(dialog_options.hDevMode);
803 }
804
805 if (dialog_options.hDevMode != NULL)
806 GlobalFree(dialog_options.hDevMode);
807 if (dialog_options.hDevNames != NULL)
808 GlobalFree(dialog_options.hDevNames);
809
[email protected]b75dca82009-10-13 18:46:21810 return context_ ? OK : FAILED;
initial.commit09911bf2008-07-26 23:55:29811}
812
[email protected]d8254622010-08-13 19:15:46813// static
[email protected]51e8d9352010-10-06 22:21:17814void PrintingContextWin::GetPrinterHelper(HANDLE printer, int level,
815 scoped_array<uint8>* buffer) {
[email protected]d8254622010-08-13 19:15:46816 DWORD buf_size = 0;
817 GetPrinter(printer, level, NULL, 0, &buf_size);
818 if (buf_size) {
819 buffer->reset(new uint8[buf_size]);
820 memset(buffer->get(), 0, buf_size);
821 if (!GetPrinter(printer, level, buffer->get(), buf_size, &buf_size)) {
822 buffer->reset();
823 }
824 }
825}
826
initial.commit09911bf2008-07-26 23:55:29827} // namespace printing