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"
20 #include "ui/aura/remote_window_tree_host_win.h"
21 #include "ui/aura/window.h"
27 void AssingResult(PrintingContext::Result
* out
, PrintingContext::Result in
) {
34 scoped_ptr
<PrintingContext
> PrintingContext::Create(Delegate
* delegate
) {
35 #if defined(ENABLE_BASIC_PRINTING)
36 return make_scoped_ptr
<PrintingContext
>(
37 new PrintingContextSytemDialogWin(delegate
));
38 #else // ENABLE_BASIC_PRINTING
39 return make_scoped_ptr
<PrintingContext
>(new PrintingContextWin(delegate
));
40 #endif // EENABLE_BASIC_PRINTING
43 PrintingContextWin::PrintingContextWin(Delegate
* delegate
)
44 : PrintingContext(delegate
), context_(NULL
) {
47 PrintingContextWin::~PrintingContextWin() {
51 void PrintingContextWin::AskUserForSettings(
55 const PrintSettingsCallback
& callback
) {
59 PrintingContext::Result
PrintingContextWin::UseDefaultSettings() {
60 DCHECK(!in_print_job_
);
62 scoped_refptr
<PrintBackend
> backend
= PrintBackend::CreateInstance(NULL
);
63 base::string16 default_printer
=
64 base::UTF8ToWide(backend
->GetDefaultPrinterName());
65 if (!default_printer
.empty()) {
66 ScopedPrinterHandle printer
;
67 if (printer
.OpenPrinter(default_printer
.c_str())) {
68 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
69 CreateDevMode(printer
.Get(), NULL
);
70 if (InitializeSettings(default_printer
, dev_mode
.get()) == OK
)
77 // No default printer configured, do we have any printers at all?
78 DWORD bytes_needed
= 0;
79 DWORD count_returned
= 0;
80 (void)::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
81 NULL
, 2, NULL
, 0, &bytes_needed
, &count_returned
);
83 DCHECK_GE(bytes_needed
, count_returned
* sizeof(PRINTER_INFO_2
));
84 scoped_ptr
<BYTE
[]> printer_info_buffer(new BYTE
[bytes_needed
]);
85 BOOL ret
= ::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
86 NULL
, 2, printer_info_buffer
.get(),
87 bytes_needed
, &bytes_needed
,
89 if (ret
&& count_returned
) { // have printers
90 // Open the first successfully found printer.
91 const PRINTER_INFO_2
* info_2
=
92 reinterpret_cast<PRINTER_INFO_2
*>(printer_info_buffer
.get());
93 const PRINTER_INFO_2
* info_2_end
= info_2
+ count_returned
;
94 for (; info_2
< info_2_end
; ++info_2
) {
95 ScopedPrinterHandle printer
;
96 if (!printer
.OpenPrinter(info_2
->pPrinterName
))
98 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
99 CreateDevMode(printer
.Get(), NULL
);
100 if (InitializeSettings(info_2
->pPrinterName
, dev_mode
.get()) == OK
)
111 gfx::Size
PrintingContextWin::GetPdfPaperSizeDeviceUnits() {
112 // Default fallback to Letter size.
113 gfx::SizeF
paper_size(kLetterWidthInch
, kLetterHeightInch
);
115 // Get settings from locale. Paper type buffer length is at most 4.
116 const int paper_type_buffer_len
= 4;
117 wchar_t paper_type_buffer
[paper_type_buffer_len
] = {0};
118 GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IPAPERSIZE
, paper_type_buffer
,
119 paper_type_buffer_len
);
120 if (wcslen(paper_type_buffer
)) { // The call succeeded.
121 int paper_code
= _wtoi(paper_type_buffer
);
122 switch (paper_code
) {
124 paper_size
.SetSize(kLegalWidthInch
, kLegalHeightInch
);
127 paper_size
.SetSize(kA4WidthInch
, kA4HeightInch
);
130 paper_size
.SetSize(kA3WidthInch
, kA3HeightInch
);
132 default: // DMPAPER_LETTER is used for default fallback.
137 paper_size
.width() * settings_
.device_units_per_inch(),
138 paper_size
.height() * settings_
.device_units_per_inch());
141 PrintingContext::Result
PrintingContextWin::UpdatePrinterSettings(
142 bool external_preview
,
143 bool show_system_dialog
) {
144 DCHECK(!in_print_job_
);
145 DCHECK(!external_preview
) << "Not implemented";
147 ScopedPrinterHandle printer
;
148 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
151 // Make printer changes local to Chrome.
152 // See MSDN documentation regarding DocumentProperties.
153 scoped_ptr
<DEVMODE
, base::FreeDeleter
> scoped_dev_mode
=
154 CreateDevModeWithColor(printer
.Get(), settings_
.device_name(),
155 settings_
.color() != GRAY
);
156 if (!scoped_dev_mode
)
160 DEVMODE
* dev_mode
= scoped_dev_mode
.get();
161 dev_mode
->dmCopies
= std::max(settings_
.copies(), 1);
162 if (dev_mode
->dmCopies
> 1) { // do not change unless multiple copies
163 dev_mode
->dmFields
|= DM_COPIES
;
164 dev_mode
->dmCollate
= settings_
.collate() ? DMCOLLATE_TRUE
:
168 switch (settings_
.duplex_mode()) {
170 dev_mode
->dmFields
|= DM_DUPLEX
;
171 dev_mode
->dmDuplex
= DMDUP_VERTICAL
;
174 dev_mode
->dmFields
|= DM_DUPLEX
;
175 dev_mode
->dmDuplex
= DMDUP_HORIZONTAL
;
178 dev_mode
->dmFields
|= DM_DUPLEX
;
179 dev_mode
->dmDuplex
= DMDUP_SIMPLEX
;
181 default: // UNKNOWN_DUPLEX_MODE
185 dev_mode
->dmFields
|= DM_ORIENTATION
;
186 dev_mode
->dmOrientation
= settings_
.landscape() ? DMORIENT_LANDSCAPE
:
189 const PrintSettings::RequestedMedia
& requested_media
=
190 settings_
.requested_media();
191 static const int kFromUm
= 100; // Windows uses 0.1mm.
192 int width
= requested_media
.size_microns
.width() / kFromUm
;
193 int height
= requested_media
.size_microns
.height() / kFromUm
;
195 if (base::StringToUint(requested_media
.vendor_id
, &id
) && id
) {
196 dev_mode
->dmFields
|= DM_PAPERSIZE
;
197 dev_mode
->dmPaperSize
= static_cast<short>(id
);
198 } else if (width
> 0 && height
> 0) {
199 dev_mode
->dmFields
|= DM_PAPERWIDTH
;
200 dev_mode
->dmPaperWidth
= width
;
201 dev_mode
->dmFields
|= DM_PAPERLENGTH
;
202 dev_mode
->dmPaperLength
= height
;
206 // Update data using DocumentProperties.
207 if (show_system_dialog
) {
208 PrintingContext::Result result
= PrintingContext::FAILED
;
209 AskUserForSettings(0, false, false, base::Bind(&AssingResult
, &result
));
212 scoped_dev_mode
= CreateDevMode(printer
.Get(), scoped_dev_mode
.get());
214 // Set printer then refresh printer settings.
215 return InitializeSettings(settings_
.device_name(), scoped_dev_mode
.get());
218 PrintingContext::Result
PrintingContextWin::InitWithSettings(
219 const PrintSettings
& settings
) {
220 DCHECK(!in_print_job_
);
222 settings_
= settings
;
224 // TODO(maruel): settings_.ToDEVMODE()
225 ScopedPrinterHandle printer
;
226 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
229 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
230 CreateDevMode(printer
.Get(), NULL
);
232 return InitializeSettings(settings_
.device_name(), dev_mode
.get());
235 PrintingContext::Result
PrintingContextWin::NewDocument(
236 const base::string16
& document_name
) {
237 DCHECK(!in_print_job_
);
241 // Set the flag used by the AbortPrintJob dialog procedure.
242 abort_printing_
= false;
244 in_print_job_
= true;
246 // Register the application's AbortProc function with GDI.
247 if (SP_ERROR
== SetAbortProc(context_
, &AbortProc
))
250 DCHECK(SimplifyDocumentTitle(document_name
) == document_name
);
251 DOCINFO di
= { sizeof(DOCINFO
) };
252 di
.lpszDocName
= document_name
.c_str();
254 // Is there a debug dump directory specified? If so, force to print to a file.
255 base::string16 debug_dump_path
=
256 PrintedDocument::CreateDebugDumpPath(document_name
,
257 FILE_PATH_LITERAL(".prn")).value();
258 if (!debug_dump_path
.empty())
259 di
.lpszOutput
= debug_dump_path
.c_str();
261 // No message loop running in unit tests.
262 DCHECK(!base::MessageLoop::current() ||
263 !base::MessageLoop::current()->NestableTasksAllowed());
265 // Begin a print job by calling the StartDoc function.
266 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
267 // IPC. Make sure recursive task processing is disabled.
268 if (StartDoc(context_
, &di
) <= 0)
274 PrintingContext::Result
PrintingContextWin::NewPage() {
278 DCHECK(in_print_job_
);
280 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
286 PrintingContext::Result
PrintingContextWin::PageDone() {
289 DCHECK(in_print_job_
);
291 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
297 PrintingContext::Result
PrintingContextWin::DocumentDone() {
300 DCHECK(in_print_job_
);
303 // Inform the driver that document has ended.
304 if (EndDoc(context_
) <= 0)
311 void PrintingContextWin::Cancel() {
312 abort_printing_
= true;
313 in_print_job_
= false;
318 void PrintingContextWin::ReleaseContext() {
325 gfx::NativeDrawingContext
PrintingContextWin::context() const {
330 BOOL
PrintingContextWin::AbortProc(HDC hdc
, int nCode
) {
332 // TODO(maruel): Need a way to find the right instance to set. Should
333 // leverage PrintJobManager here?
334 // abort_printing_ = true;
339 PrintingContext::Result
PrintingContextWin::InitializeSettings(
340 const std::wstring
& device_name
,
346 context_
= CreateDC(L
"WINSPOOL", device_name
.c_str(), NULL
, dev_mode
);
350 skia::InitializeDC(context_
);
352 DCHECK(!in_print_job_
);
353 settings_
.set_device_name(device_name
);
354 PrintSettingsInitializerWin::InitPrintSettings(
355 context_
, *dev_mode
, &settings_
);
360 HWND
PrintingContextWin::GetRootWindow(gfx::NativeView view
) {
362 if (view
&& view
->GetHost())
363 window
= view
->GetHost()->GetAcceleratedWidget();
365 // TODO(maruel): crbug.com/1214347 Get the right browser window instead.
366 return GetDesktopWindow();
371 scoped_ptr
<DEVMODE
, base::FreeDeleter
> PrintingContextWin::ShowPrintDialog(
373 gfx::NativeView parent_view
,
375 // Note that this cannot use ui::BaseShellDialog as the print dialog is
376 // system modal: opening it from a background thread can cause Windows to
377 // get the wrong Z-order which will make the print dialog appear behind the
378 // browser frame (but still being modal) so neither the browser frame nor
379 // the print dialog will get any input. See http://crbug.com/342697
380 // http://crbug.com/180997 for details.
381 base::MessageLoop::ScopedNestableTaskAllower
allow(
382 base::MessageLoop::current());
384 bool canceled
= false;
385 scoped_ptr
<DEVMODE
, base::FreeDeleter
> result
=
386 PromptDevMode(printer
,
387 settings_
.device_name(),
389 GetRootWindow(parent_view
),
394 abort_printing_
= true;
397 return result
.Pass();
400 } // namespace printing