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.
5 #include "printing/printing_context_win.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "printing/backend/print_backend.h"
13 #include "printing/backend/win_helper.h"
14 #include "printing/print_settings_initializer_win.h"
15 #include "printing/printed_document.h"
16 #include "printing/printing_context_system_dialog_win.h"
17 #include "printing/printing_utils.h"
18 #include "printing/units.h"
19 #include "skia/ext/platform_device.h"
22 #include "ui/aura/remote_window_tree_host_win.h"
23 #include "ui/aura/window.h"
30 void AssingResult(PrintingContext::Result
* out
, PrintingContext::Result in
) {
37 scoped_ptr
<PrintingContext
> PrintingContext::Create(Delegate
* delegate
) {
38 #if defined(ENABLE_BASIC_PRINTING)
39 return make_scoped_ptr
<PrintingContext
>(
40 new PrintingContextSytemDialogWin(delegate
));
41 #else // ENABLE_BASIC_PRINTING
42 return make_scoped_ptr
<PrintingContext
>(new PrintingContextWin(delegate
));
43 #endif // EENABLE_BASIC_PRINTING
46 PrintingContextWin::PrintingContextWin(Delegate
* delegate
)
47 : PrintingContext(delegate
), context_(NULL
) {
50 PrintingContextWin::~PrintingContextWin() {
54 void PrintingContextWin::AskUserForSettings(
58 const PrintSettingsCallback
& callback
) {
62 PrintingContext::Result
PrintingContextWin::UseDefaultSettings() {
63 DCHECK(!in_print_job_
);
65 scoped_refptr
<PrintBackend
> backend
= PrintBackend::CreateInstance(NULL
);
66 base::string16 default_printer
=
67 base::UTF8ToWide(backend
->GetDefaultPrinterName());
68 if (!default_printer
.empty()) {
69 ScopedPrinterHandle printer
;
70 if (printer
.OpenPrinter(default_printer
.c_str())) {
71 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
72 CreateDevMode(printer
.Get(), NULL
);
73 if (InitializeSettings(default_printer
, dev_mode
.get()) == OK
)
80 // No default printer configured, do we have any printers at all?
81 DWORD bytes_needed
= 0;
82 DWORD count_returned
= 0;
83 (void)::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
84 NULL
, 2, NULL
, 0, &bytes_needed
, &count_returned
);
86 DCHECK_GE(bytes_needed
, count_returned
* sizeof(PRINTER_INFO_2
));
87 scoped_ptr
<BYTE
[]> printer_info_buffer(new BYTE
[bytes_needed
]);
88 BOOL ret
= ::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
89 NULL
, 2, printer_info_buffer
.get(),
90 bytes_needed
, &bytes_needed
,
92 if (ret
&& count_returned
) { // have printers
93 // Open the first successfully found printer.
94 const PRINTER_INFO_2
* info_2
=
95 reinterpret_cast<PRINTER_INFO_2
*>(printer_info_buffer
.get());
96 const PRINTER_INFO_2
* info_2_end
= info_2
+ count_returned
;
97 for (; info_2
< info_2_end
; ++info_2
) {
98 ScopedPrinterHandle printer
;
99 if (!printer
.OpenPrinter(info_2
->pPrinterName
))
101 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
102 CreateDevMode(printer
.Get(), NULL
);
103 if (InitializeSettings(info_2
->pPrinterName
, dev_mode
.get()) == OK
)
114 gfx::Size
PrintingContextWin::GetPdfPaperSizeDeviceUnits() {
115 // Default fallback to Letter size.
116 gfx::SizeF
paper_size(kLetterWidthInch
, kLetterHeightInch
);
118 // Get settings from locale. Paper type buffer length is at most 4.
119 const int paper_type_buffer_len
= 4;
120 wchar_t paper_type_buffer
[paper_type_buffer_len
] = {0};
121 GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IPAPERSIZE
, paper_type_buffer
,
122 paper_type_buffer_len
);
123 if (wcslen(paper_type_buffer
)) { // The call succeeded.
124 int paper_code
= _wtoi(paper_type_buffer
);
125 switch (paper_code
) {
127 paper_size
.SetSize(kLegalWidthInch
, kLegalHeightInch
);
130 paper_size
.SetSize(kA4WidthInch
, kA4HeightInch
);
133 paper_size
.SetSize(kA3WidthInch
, kA3HeightInch
);
135 default: // DMPAPER_LETTER is used for default fallback.
140 paper_size
.width() * settings_
.device_units_per_inch(),
141 paper_size
.height() * settings_
.device_units_per_inch());
144 PrintingContext::Result
PrintingContextWin::UpdatePrinterSettings(
145 bool external_preview
,
146 bool show_system_dialog
) {
147 DCHECK(!in_print_job_
);
148 DCHECK(!external_preview
) << "Not implemented";
150 ScopedPrinterHandle printer
;
151 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
154 // Make printer changes local to Chrome.
155 // See MSDN documentation regarding DocumentProperties.
156 scoped_ptr
<DEVMODE
, base::FreeDeleter
> scoped_dev_mode
=
157 CreateDevModeWithColor(printer
.Get(), settings_
.device_name(),
158 settings_
.color() != GRAY
);
159 if (!scoped_dev_mode
)
163 DEVMODE
* dev_mode
= scoped_dev_mode
.get();
164 dev_mode
->dmCopies
= std::max(settings_
.copies(), 1);
165 if (dev_mode
->dmCopies
> 1) { // do not change unless multiple copies
166 dev_mode
->dmFields
|= DM_COPIES
;
167 dev_mode
->dmCollate
= settings_
.collate() ? DMCOLLATE_TRUE
:
171 switch (settings_
.duplex_mode()) {
173 dev_mode
->dmFields
|= DM_DUPLEX
;
174 dev_mode
->dmDuplex
= DMDUP_VERTICAL
;
177 dev_mode
->dmFields
|= DM_DUPLEX
;
178 dev_mode
->dmDuplex
= DMDUP_HORIZONTAL
;
181 dev_mode
->dmFields
|= DM_DUPLEX
;
182 dev_mode
->dmDuplex
= DMDUP_SIMPLEX
;
184 default: // UNKNOWN_DUPLEX_MODE
188 dev_mode
->dmFields
|= DM_ORIENTATION
;
189 dev_mode
->dmOrientation
= settings_
.landscape() ? DMORIENT_LANDSCAPE
:
192 const PrintSettings::RequestedMedia
& requested_media
=
193 settings_
.requested_media();
194 static const int kFromUm
= 100; // Windows uses 0.1mm.
195 int width
= requested_media
.size_microns
.width() / kFromUm
;
196 int height
= requested_media
.size_microns
.height() / kFromUm
;
198 if (base::StringToUint(requested_media
.vendor_id
, &id
) && id
) {
199 dev_mode
->dmFields
|= DM_PAPERSIZE
;
200 dev_mode
->dmPaperSize
= static_cast<short>(id
);
201 } else if (width
> 0 && height
> 0) {
202 dev_mode
->dmFields
|= DM_PAPERWIDTH
;
203 dev_mode
->dmPaperWidth
= width
;
204 dev_mode
->dmFields
|= DM_PAPERLENGTH
;
205 dev_mode
->dmPaperLength
= height
;
209 // Update data using DocumentProperties.
210 if (show_system_dialog
) {
211 PrintingContext::Result result
= PrintingContext::FAILED
;
212 AskUserForSettings(0, false, false, base::Bind(&AssingResult
, &result
));
215 scoped_dev_mode
= CreateDevMode(printer
.Get(), scoped_dev_mode
.get());
217 // Set printer then refresh printer settings.
218 return InitializeSettings(settings_
.device_name(), scoped_dev_mode
.get());
221 PrintingContext::Result
PrintingContextWin::InitWithSettings(
222 const PrintSettings
& settings
) {
223 DCHECK(!in_print_job_
);
225 settings_
= settings
;
227 // TODO(maruel): settings_.ToDEVMODE()
228 ScopedPrinterHandle printer
;
229 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
232 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
233 CreateDevMode(printer
.Get(), NULL
);
235 return InitializeSettings(settings_
.device_name(), dev_mode
.get());
238 PrintingContext::Result
PrintingContextWin::NewDocument(
239 const base::string16
& document_name
) {
240 DCHECK(!in_print_job_
);
244 // Set the flag used by the AbortPrintJob dialog procedure.
245 abort_printing_
= false;
247 in_print_job_
= true;
249 // Register the application's AbortProc function with GDI.
250 if (SP_ERROR
== SetAbortProc(context_
, &AbortProc
))
253 DCHECK(SimplifyDocumentTitle(document_name
) == document_name
);
254 DOCINFO di
= { sizeof(DOCINFO
) };
255 di
.lpszDocName
= document_name
.c_str();
257 // Is there a debug dump directory specified? If so, force to print to a file.
258 base::string16 debug_dump_path
=
259 PrintedDocument::CreateDebugDumpPath(document_name
,
260 FILE_PATH_LITERAL(".prn")).value();
261 if (!debug_dump_path
.empty())
262 di
.lpszOutput
= debug_dump_path
.c_str();
264 // No message loop running in unit tests.
265 DCHECK(!base::MessageLoop::current() ||
266 !base::MessageLoop::current()->NestableTasksAllowed());
268 // Begin a print job by calling the StartDoc function.
269 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
270 // IPC. Make sure recursive task processing is disabled.
271 if (StartDoc(context_
, &di
) <= 0)
277 PrintingContext::Result
PrintingContextWin::NewPage() {
281 DCHECK(in_print_job_
);
283 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
289 PrintingContext::Result
PrintingContextWin::PageDone() {
292 DCHECK(in_print_job_
);
294 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
300 PrintingContext::Result
PrintingContextWin::DocumentDone() {
303 DCHECK(in_print_job_
);
306 // Inform the driver that document has ended.
307 if (EndDoc(context_
) <= 0)
314 void PrintingContextWin::Cancel() {
315 abort_printing_
= true;
316 in_print_job_
= false;
321 void PrintingContextWin::ReleaseContext() {
328 gfx::NativeDrawingContext
PrintingContextWin::context() const {
333 BOOL
PrintingContextWin::AbortProc(HDC hdc
, int nCode
) {
335 // TODO(maruel): Need a way to find the right instance to set. Should
336 // leverage PrintJobManager here?
337 // abort_printing_ = true;
342 PrintingContext::Result
PrintingContextWin::InitializeSettings(
343 const std::wstring
& device_name
,
349 context_
= CreateDC(L
"WINSPOOL", device_name
.c_str(), NULL
, dev_mode
);
353 skia::InitializeDC(context_
);
355 DCHECK(!in_print_job_
);
356 settings_
.set_device_name(device_name
);
357 PrintSettingsInitializerWin::InitPrintSettings(
358 context_
, *dev_mode
, &settings_
);
363 HWND
PrintingContextWin::GetRootWindow(gfx::NativeView view
) {
365 #if defined(USE_AURA)
367 window
= view
->GetHost()->GetAcceleratedWidget();
369 if (view
&& IsWindow(view
)) {
370 window
= GetAncestor(view
, GA_ROOTOWNER
);
374 // TODO(maruel): crbug.com/1214347 Get the right browser window instead.
375 return GetDesktopWindow();
380 scoped_ptr
<DEVMODE
, base::FreeDeleter
> PrintingContextWin::ShowPrintDialog(
382 gfx::NativeView parent_view
,
384 // Note that this cannot use ui::BaseShellDialog as the print dialog is
385 // system modal: opening it from a background thread can cause Windows to
386 // get the wrong Z-order which will make the print dialog appear behind the
387 // browser frame (but still being modal) so neither the browser frame nor
388 // the print dialog will get any input. See http://crbug.com/342697
389 // http://crbug.com/180997 for details.
390 base::MessageLoop::ScopedNestableTaskAllower
allow(
391 base::MessageLoop::current());
393 bool canceled
= false;
394 scoped_ptr
<DEVMODE
, base::FreeDeleter
> result
=
395 PromptDevMode(printer
,
396 settings_
.device_name(),
398 GetRootWindow(parent_view
),
403 abort_printing_
= true;
406 return result
.Pass();
409 } // namespace printing