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
,
145 DCHECK(!in_print_job_
);
146 DCHECK(!external_preview
) << "Not implemented";
148 ScopedPrinterHandle printer
;
149 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
152 // Make printer changes local to Chrome.
153 // See MSDN documentation regarding DocumentProperties.
154 scoped_ptr
<DEVMODE
, base::FreeDeleter
> scoped_dev_mode
=
155 CreateDevModeWithColor(printer
.Get(), settings_
.device_name(),
156 settings_
.color() != GRAY
);
157 if (!scoped_dev_mode
)
161 DEVMODE
* dev_mode
= scoped_dev_mode
.get();
162 dev_mode
->dmCopies
= std::max(settings_
.copies(), 1);
163 if (dev_mode
->dmCopies
> 1) { // do not change unless multiple copies
164 dev_mode
->dmFields
|= DM_COPIES
;
165 dev_mode
->dmCollate
= settings_
.collate() ? DMCOLLATE_TRUE
:
169 switch (settings_
.duplex_mode()) {
171 dev_mode
->dmFields
|= DM_DUPLEX
;
172 dev_mode
->dmDuplex
= DMDUP_VERTICAL
;
175 dev_mode
->dmFields
|= DM_DUPLEX
;
176 dev_mode
->dmDuplex
= DMDUP_HORIZONTAL
;
179 dev_mode
->dmFields
|= DM_DUPLEX
;
180 dev_mode
->dmDuplex
= DMDUP_SIMPLEX
;
182 default: // UNKNOWN_DUPLEX_MODE
186 dev_mode
->dmFields
|= DM_ORIENTATION
;
187 dev_mode
->dmOrientation
= settings_
.landscape() ? DMORIENT_LANDSCAPE
:
190 const PrintSettings::RequestedMedia
& requested_media
=
191 settings_
.requested_media();
192 static const int kFromUm
= 100; // Windows uses 0.1mm.
193 int width
= requested_media
.size_microns
.width() / kFromUm
;
194 int height
= requested_media
.size_microns
.height() / kFromUm
;
196 if (base::StringToUint(requested_media
.vendor_id
, &id
) && id
) {
197 dev_mode
->dmFields
|= DM_PAPERSIZE
;
198 dev_mode
->dmPaperSize
= static_cast<short>(id
);
199 } else if (width
> 0 && height
> 0) {
200 dev_mode
->dmFields
|= DM_PAPERWIDTH
;
201 dev_mode
->dmPaperWidth
= width
;
202 dev_mode
->dmFields
|= DM_PAPERLENGTH
;
203 dev_mode
->dmPaperLength
= height
;
207 // Update data using DocumentProperties.
208 if (show_system_dialog
) {
209 PrintingContext::Result result
= PrintingContext::FAILED
;
210 AskUserForSettings(page_count
, false, false,
211 base::Bind(&AssingResult
, &result
));
214 scoped_dev_mode
= CreateDevMode(printer
.Get(), scoped_dev_mode
.get());
216 // Set printer then refresh printer settings.
217 return InitializeSettings(settings_
.device_name(), scoped_dev_mode
.get());
220 PrintingContext::Result
PrintingContextWin::InitWithSettings(
221 const PrintSettings
& settings
) {
222 DCHECK(!in_print_job_
);
224 settings_
= settings
;
226 // TODO(maruel): settings_.ToDEVMODE()
227 ScopedPrinterHandle printer
;
228 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
231 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
232 CreateDevMode(printer
.Get(), NULL
);
234 return InitializeSettings(settings_
.device_name(), dev_mode
.get());
237 PrintingContext::Result
PrintingContextWin::NewDocument(
238 const base::string16
& document_name
) {
239 DCHECK(!in_print_job_
);
243 // Set the flag used by the AbortPrintJob dialog procedure.
244 abort_printing_
= false;
246 in_print_job_
= true;
248 // Register the application's AbortProc function with GDI.
249 if (SP_ERROR
== SetAbortProc(context_
, &AbortProc
))
252 DCHECK(SimplifyDocumentTitle(document_name
) == document_name
);
253 DOCINFO di
= { sizeof(DOCINFO
) };
254 di
.lpszDocName
= document_name
.c_str();
256 // Is there a debug dump directory specified? If so, force to print to a file.
257 base::string16 debug_dump_path
=
258 PrintedDocument::CreateDebugDumpPath(document_name
,
259 FILE_PATH_LITERAL(".prn")).value();
260 if (!debug_dump_path
.empty())
261 di
.lpszOutput
= debug_dump_path
.c_str();
263 // No message loop running in unit tests.
264 DCHECK(!base::MessageLoop::current() ||
265 !base::MessageLoop::current()->NestableTasksAllowed());
267 // Begin a print job by calling the StartDoc function.
268 // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
269 // IPC. Make sure recursive task processing is disabled.
270 if (StartDoc(context_
, &di
) <= 0)
276 PrintingContext::Result
PrintingContextWin::NewPage() {
280 DCHECK(in_print_job_
);
282 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
288 PrintingContext::Result
PrintingContextWin::PageDone() {
291 DCHECK(in_print_job_
);
293 // Intentional No-op. PdfMetafileSkia::SafePlayback takes care of calling
299 PrintingContext::Result
PrintingContextWin::DocumentDone() {
302 DCHECK(in_print_job_
);
305 // Inform the driver that document has ended.
306 if (EndDoc(context_
) <= 0)
313 void PrintingContextWin::Cancel() {
314 abort_printing_
= true;
315 in_print_job_
= false;
320 void PrintingContextWin::ReleaseContext() {
327 gfx::NativeDrawingContext
PrintingContextWin::context() const {
332 BOOL
PrintingContextWin::AbortProc(HDC hdc
, int nCode
) {
334 // TODO(maruel): Need a way to find the right instance to set. Should
335 // leverage PrintJobManager here?
336 // abort_printing_ = true;
341 PrintingContext::Result
PrintingContextWin::InitializeSettings(
342 const std::wstring
& device_name
,
348 context_
= CreateDC(L
"WINSPOOL", device_name
.c_str(), NULL
, dev_mode
);
352 skia::InitializeDC(context_
);
354 DCHECK(!in_print_job_
);
355 settings_
.set_device_name(device_name
);
356 PrintSettingsInitializerWin::InitPrintSettings(
357 context_
, *dev_mode
, &settings_
);
362 HWND
PrintingContextWin::GetRootWindow(gfx::NativeView view
) {
364 if (view
&& view
->GetHost())
365 window
= view
->GetHost()->GetAcceleratedWidget();
367 // TODO(maruel): crbug.com/1214347 Get the right browser window instead.
368 return GetDesktopWindow();
373 scoped_ptr
<DEVMODE
, base::FreeDeleter
> PrintingContextWin::ShowPrintDialog(
375 gfx::NativeView parent_view
,
377 // Note that this cannot use ui::BaseShellDialog as the print dialog is
378 // system modal: opening it from a background thread can cause Windows to
379 // get the wrong Z-order which will make the print dialog appear behind the
380 // browser frame (but still being modal) so neither the browser frame nor
381 // the print dialog will get any input. See http://crbug.com/342697
382 // http://crbug.com/180997 for details.
383 base::MessageLoop::ScopedNestableTaskAllower
allow(
384 base::MessageLoop::current());
386 bool canceled
= false;
387 scoped_ptr
<DEVMODE
, base::FreeDeleter
> result
=
388 PromptDevMode(printer
,
389 settings_
.device_name(),
391 GetRootWindow(parent_view
),
396 abort_printing_
= true;
399 return result
.Pass();
402 } // namespace printing