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"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "printing/backend/print_backend.h"
12 #include "printing/backend/win_helper.h"
13 #include "printing/print_settings_initializer_win.h"
14 #include "printing/printed_document.h"
15 #include "printing/printing_context_system_dialog_win.h"
16 #include "printing/printing_utils.h"
17 #include "printing/units.h"
18 #include "skia/ext/platform_device.h"
21 #include "ui/aura/remote_window_tree_host_win.h"
22 #include "ui/aura/window.h"
28 scoped_ptr
<PrintingContext
> PrintingContext::Create(Delegate
* delegate
) {
29 #if defined(DISABLE_BASIC_PRINTING)
30 return make_scoped_ptr
<PrintingContext
>(new PrintingContextWin(delegate
));
31 #else // DISABLE_BASIC_PRINTING
32 return make_scoped_ptr
<PrintingContext
>(
33 new PrintingContextSytemDialogWin(delegate
));
34 #endif // DISABLE_BASIC_PRINTING
37 PrintingContextWin::PrintingContextWin(Delegate
* delegate
)
38 : PrintingContext(delegate
), context_(NULL
) {
41 PrintingContextWin::~PrintingContextWin() {
45 void PrintingContextWin::AskUserForSettings(
48 const PrintSettingsCallback
& callback
) {
52 PrintingContext::Result
PrintingContextWin::UseDefaultSettings() {
53 DCHECK(!in_print_job_
);
55 scoped_refptr
<PrintBackend
> backend
= PrintBackend::CreateInstance(NULL
);
56 base::string16 default_printer
=
57 base::UTF8ToWide(backend
->GetDefaultPrinterName());
58 if (!default_printer
.empty()) {
59 ScopedPrinterHandle printer
;
60 if (printer
.OpenPrinter(default_printer
.c_str())) {
61 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
62 CreateDevMode(printer
.Get(), NULL
);
63 if (InitializeSettings(default_printer
, dev_mode
.get()) == OK
)
70 // No default printer configured, do we have any printers at all?
71 DWORD bytes_needed
= 0;
72 DWORD count_returned
= 0;
73 (void)::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
74 NULL
, 2, NULL
, 0, &bytes_needed
, &count_returned
);
76 DCHECK_GE(bytes_needed
, count_returned
* sizeof(PRINTER_INFO_2
));
77 scoped_ptr
<BYTE
[]> printer_info_buffer(new BYTE
[bytes_needed
]);
78 BOOL ret
= ::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
79 NULL
, 2, printer_info_buffer
.get(),
80 bytes_needed
, &bytes_needed
,
82 if (ret
&& count_returned
) { // have printers
83 // Open the first successfully found printer.
84 const PRINTER_INFO_2
* info_2
=
85 reinterpret_cast<PRINTER_INFO_2
*>(printer_info_buffer
.get());
86 const PRINTER_INFO_2
* info_2_end
= info_2
+ count_returned
;
87 for (; info_2
< info_2_end
; ++info_2
) {
88 ScopedPrinterHandle printer
;
89 if (!printer
.OpenPrinter(info_2
->pPrinterName
))
91 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
92 CreateDevMode(printer
.Get(), NULL
);
93 if (InitializeSettings(info_2
->pPrinterName
, dev_mode
.get()) == OK
)
104 gfx::Size
PrintingContextWin::GetPdfPaperSizeDeviceUnits() {
105 // Default fallback to Letter size.
106 gfx::SizeF
paper_size(kLetterWidthInch
, kLetterHeightInch
);
108 // Get settings from locale. Paper type buffer length is at most 4.
109 const int paper_type_buffer_len
= 4;
110 wchar_t paper_type_buffer
[paper_type_buffer_len
] = {0};
111 GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IPAPERSIZE
, paper_type_buffer
,
112 paper_type_buffer_len
);
113 if (wcslen(paper_type_buffer
)) { // The call succeeded.
114 int paper_code
= _wtoi(paper_type_buffer
);
115 switch (paper_code
) {
117 paper_size
.SetSize(kLegalWidthInch
, kLegalHeightInch
);
120 paper_size
.SetSize(kA4WidthInch
, kA4HeightInch
);
123 paper_size
.SetSize(kA3WidthInch
, kA3HeightInch
);
125 default: // DMPAPER_LETTER is used for default fallback.
130 paper_size
.width() * settings_
.device_units_per_inch(),
131 paper_size
.height() * settings_
.device_units_per_inch());
134 PrintingContext::Result
PrintingContextWin::UpdatePrinterSettings(
135 bool external_preview
,
136 bool show_system_dialog
) {
137 DCHECK(!in_print_job_
);
138 DCHECK(!external_preview
) << "Not implemented";
140 ScopedPrinterHandle printer
;
141 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
144 // Make printer changes local to Chrome.
145 // See MSDN documentation regarding DocumentProperties.
146 scoped_ptr
<DEVMODE
, base::FreeDeleter
> scoped_dev_mode
=
147 CreateDevModeWithColor(printer
.Get(), settings_
.device_name(),
148 settings_
.color() != GRAY
);
149 if (!scoped_dev_mode
)
153 DEVMODE
* dev_mode
= scoped_dev_mode
.get();
154 dev_mode
->dmCopies
= std::max(settings_
.copies(), 1);
155 if (dev_mode
->dmCopies
> 1) { // do not change unless multiple copies
156 dev_mode
->dmFields
|= DM_COPIES
;
157 dev_mode
->dmCollate
= settings_
.collate() ? DMCOLLATE_TRUE
:
161 switch (settings_
.duplex_mode()) {
163 dev_mode
->dmFields
|= DM_DUPLEX
;
164 dev_mode
->dmDuplex
= DMDUP_VERTICAL
;
167 dev_mode
->dmFields
|= DM_DUPLEX
;
168 dev_mode
->dmDuplex
= DMDUP_HORIZONTAL
;
171 dev_mode
->dmFields
|= DM_DUPLEX
;
172 dev_mode
->dmDuplex
= DMDUP_SIMPLEX
;
174 default: // UNKNOWN_DUPLEX_MODE
178 dev_mode
->dmFields
|= DM_ORIENTATION
;
179 dev_mode
->dmOrientation
= settings_
.landscape() ? DMORIENT_LANDSCAPE
:
182 const PrintSettings::RequestedMedia
& requested_media
=
183 settings_
.requested_media();
184 static const int kFromUm
= 100; // Windows uses 0.1mm.
185 int width
= requested_media
.size_microns
.width() / kFromUm
;
186 int height
= requested_media
.size_microns
.height() / kFromUm
;
188 if (base::StringToUint(requested_media
.vendor_id
, &id
) && id
) {
189 dev_mode
->dmFields
|= DM_PAPERSIZE
;
190 dev_mode
->dmPaperSize
= static_cast<short>(id
);
191 } else if (width
> 0 && height
> 0) {
192 dev_mode
->dmFields
|= DM_PAPERWIDTH
;
193 dev_mode
->dmPaperWidth
= width
;
194 dev_mode
->dmFields
|= DM_PAPERLENGTH
;
195 dev_mode
->dmPaperLength
= height
;
199 // Update data using DocumentProperties.
200 if (show_system_dialog
) {
201 scoped_dev_mode
= ShowPrintDialog(
202 printer
.Get(), delegate_
->GetParentView(), scoped_dev_mode
.get());
204 scoped_dev_mode
= CreateDevMode(printer
.Get(), scoped_dev_mode
.get());
206 // Set printer then refresh printer settings.
207 return InitializeSettings(settings_
.device_name(), scoped_dev_mode
.get());
210 PrintingContext::Result
PrintingContextWin::InitWithSettings(
211 const PrintSettings
& settings
) {
212 DCHECK(!in_print_job_
);
214 settings_
= settings
;
216 // TODO(maruel): settings_.ToDEVMODE()
217 ScopedPrinterHandle printer
;
218 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
221 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
222 CreateDevMode(printer
.Get(), NULL
);
224 return InitializeSettings(settings_
.device_name(), dev_mode
.get());
227 PrintingContext::Result
PrintingContextWin::NewDocument(
228 const base::string16
& document_name
) {
229 DCHECK(!in_print_job_
);
233 // Set the flag used by the AbortPrintJob dialog procedure.
234 abort_printing_
= false;
236 in_print_job_
= true;
238 // Register the application's AbortProc function with GDI.
239 if (SP_ERROR
== SetAbortProc(context_
, &AbortProc
))
242 DCHECK(SimplifyDocumentTitle(document_name
) == document_name
);
243 DOCINFO di
= { sizeof(DOCINFO
) };
244 di
.lpszDocName
= document_name
.c_str();
246 // Is there a debug dump directory specified? If so, force to print to a file.
247 base::string16 debug_dump_path
=
248 PrintedDocument::CreateDebugDumpPath(document_name
,
249 FILE_PATH_LITERAL(".prn")).value();
250 if (!debug_dump_path
.empty())
251 di
.lpszOutput
= debug_dump_path
.c_str();
253 // No message loop running in unit tests.
254 DCHECK(!base::MessageLoop::current() ||
255 !base::MessageLoop::current()->NestableTasksAllowed());
257 // Begin a print job by calling the StartDoc function.
258 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
259 // IPC. Make sure recursive task processing is disabled.
260 if (StartDoc(context_
, &di
) <= 0)
266 PrintingContext::Result
PrintingContextWin::NewPage() {
270 DCHECK(in_print_job_
);
272 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
278 PrintingContext::Result
PrintingContextWin::PageDone() {
281 DCHECK(in_print_job_
);
283 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
289 PrintingContext::Result
PrintingContextWin::DocumentDone() {
292 DCHECK(in_print_job_
);
295 // Inform the driver that document has ended.
296 if (EndDoc(context_
) <= 0)
303 void PrintingContextWin::Cancel() {
304 abort_printing_
= true;
305 in_print_job_
= false;
310 void PrintingContextWin::ReleaseContext() {
317 gfx::NativeDrawingContext
PrintingContextWin::context() const {
322 BOOL
PrintingContextWin::AbortProc(HDC hdc
, int nCode
) {
324 // TODO(maruel): Need a way to find the right instance to set. Should
325 // leverage PrintJobManager here?
326 // abort_printing_ = true;
331 PrintingContext::Result
PrintingContextWin::InitializeSettings(
332 const std::wstring
& device_name
,
338 context_
= CreateDC(L
"WINSPOOL", device_name
.c_str(), NULL
, dev_mode
);
342 skia::InitializeDC(context_
);
344 DCHECK(!in_print_job_
);
345 settings_
.set_device_name(device_name
);
346 PrintSettingsInitializerWin::InitPrintSettings(
347 context_
, *dev_mode
, &settings_
);
352 HWND
PrintingContextWin::GetRootWindow(gfx::NativeView view
) {
354 #if defined(USE_AURA)
356 window
= view
->GetHost()->GetAcceleratedWidget();
358 if (view
&& IsWindow(view
)) {
359 window
= GetAncestor(view
, GA_ROOTOWNER
);
363 // TODO(maruel): crbug.com/1214347 Get the right browser window instead.
364 return GetDesktopWindow();
369 scoped_ptr
<DEVMODE
, base::FreeDeleter
> PrintingContextWin::ShowPrintDialog(
371 gfx::NativeView parent_view
,
373 // Note that this cannot use ui::BaseShellDialog as the print dialog is
374 // system modal: opening it from a background thread can cause Windows to
375 // get the wrong Z-order which will make the print dialog appear behind the
376 // browser frame (but still being modal) so neither the browser frame nor
377 // the print dialog will get any input. See http://crbug.com/342697
378 // http://crbug.com/180997 for details.
379 base::MessageLoop::ScopedNestableTaskAllower
allow(
380 base::MessageLoop::current());
382 bool canceled
= false;
383 scoped_ptr
<DEVMODE
, base::FreeDeleter
> result
=
384 PromptDevMode(printer
,
385 settings_
.device_name(),
387 GetRootWindow(parent_view
),
392 abort_printing_
= true;
395 return result
.Pass();
398 } // namespace printing