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(
57 const PrintSettingsCallback
& callback
) {
61 PrintingContext::Result
PrintingContextWin::UseDefaultSettings() {
62 DCHECK(!in_print_job_
);
64 scoped_refptr
<PrintBackend
> backend
= PrintBackend::CreateInstance(NULL
);
65 base::string16 default_printer
=
66 base::UTF8ToWide(backend
->GetDefaultPrinterName());
67 if (!default_printer
.empty()) {
68 ScopedPrinterHandle printer
;
69 if (printer
.OpenPrinter(default_printer
.c_str())) {
70 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
71 CreateDevMode(printer
.Get(), NULL
);
72 if (InitializeSettings(default_printer
, dev_mode
.get()) == OK
)
79 // No default printer configured, do we have any printers at all?
80 DWORD bytes_needed
= 0;
81 DWORD count_returned
= 0;
82 (void)::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
83 NULL
, 2, NULL
, 0, &bytes_needed
, &count_returned
);
85 DCHECK_GE(bytes_needed
, count_returned
* sizeof(PRINTER_INFO_2
));
86 scoped_ptr
<BYTE
[]> printer_info_buffer(new BYTE
[bytes_needed
]);
87 BOOL ret
= ::EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
,
88 NULL
, 2, printer_info_buffer
.get(),
89 bytes_needed
, &bytes_needed
,
91 if (ret
&& count_returned
) { // have printers
92 // Open the first successfully found printer.
93 const PRINTER_INFO_2
* info_2
=
94 reinterpret_cast<PRINTER_INFO_2
*>(printer_info_buffer
.get());
95 const PRINTER_INFO_2
* info_2_end
= info_2
+ count_returned
;
96 for (; info_2
< info_2_end
; ++info_2
) {
97 ScopedPrinterHandle printer
;
98 if (!printer
.OpenPrinter(info_2
->pPrinterName
))
100 scoped_ptr
<DEVMODE
, base::FreeDeleter
> dev_mode
=
101 CreateDevMode(printer
.Get(), NULL
);
102 if (InitializeSettings(info_2
->pPrinterName
, dev_mode
.get()) == OK
)
113 gfx::Size
PrintingContextWin::GetPdfPaperSizeDeviceUnits() {
114 // Default fallback to Letter size.
115 gfx::SizeF
paper_size(kLetterWidthInch
, kLetterHeightInch
);
117 // Get settings from locale. Paper type buffer length is at most 4.
118 const int paper_type_buffer_len
= 4;
119 wchar_t paper_type_buffer
[paper_type_buffer_len
] = {0};
120 GetLocaleInfo(LOCALE_USER_DEFAULT
, LOCALE_IPAPERSIZE
, paper_type_buffer
,
121 paper_type_buffer_len
);
122 if (wcslen(paper_type_buffer
)) { // The call succeeded.
123 int paper_code
= _wtoi(paper_type_buffer
);
124 switch (paper_code
) {
126 paper_size
.SetSize(kLegalWidthInch
, kLegalHeightInch
);
129 paper_size
.SetSize(kA4WidthInch
, kA4HeightInch
);
132 paper_size
.SetSize(kA3WidthInch
, kA3HeightInch
);
134 default: // DMPAPER_LETTER is used for default fallback.
139 paper_size
.width() * settings_
.device_units_per_inch(),
140 paper_size
.height() * settings_
.device_units_per_inch());
143 PrintingContext::Result
PrintingContextWin::UpdatePrinterSettings(
144 bool external_preview
,
145 bool show_system_dialog
) {
146 DCHECK(!in_print_job_
);
147 DCHECK(!external_preview
) << "Not implemented";
149 ScopedPrinterHandle printer
;
150 if (!printer
.OpenPrinter(settings_
.device_name().c_str()))
153 // Make printer changes local to Chrome.
154 // See MSDN documentation regarding DocumentProperties.
155 scoped_ptr
<DEVMODE
, base::FreeDeleter
> scoped_dev_mode
=
156 CreateDevModeWithColor(printer
.Get(), settings_
.device_name(),
157 settings_
.color() != GRAY
);
158 if (!scoped_dev_mode
)
162 DEVMODE
* dev_mode
= scoped_dev_mode
.get();
163 dev_mode
->dmCopies
= std::max(settings_
.copies(), 1);
164 if (dev_mode
->dmCopies
> 1) { // do not change unless multiple copies
165 dev_mode
->dmFields
|= DM_COPIES
;
166 dev_mode
->dmCollate
= settings_
.collate() ? DMCOLLATE_TRUE
:
170 switch (settings_
.duplex_mode()) {
172 dev_mode
->dmFields
|= DM_DUPLEX
;
173 dev_mode
->dmDuplex
= DMDUP_VERTICAL
;
176 dev_mode
->dmFields
|= DM_DUPLEX
;
177 dev_mode
->dmDuplex
= DMDUP_HORIZONTAL
;
180 dev_mode
->dmFields
|= DM_DUPLEX
;
181 dev_mode
->dmDuplex
= DMDUP_SIMPLEX
;
183 default: // UNKNOWN_DUPLEX_MODE
187 dev_mode
->dmFields
|= DM_ORIENTATION
;
188 dev_mode
->dmOrientation
= settings_
.landscape() ? DMORIENT_LANDSCAPE
:
191 const PrintSettings::RequestedMedia
& requested_media
=
192 settings_
.requested_media();
193 static const int kFromUm
= 100; // Windows uses 0.1mm.
194 int width
= requested_media
.size_microns
.width() / kFromUm
;
195 int height
= requested_media
.size_microns
.height() / kFromUm
;
197 if (base::StringToUint(requested_media
.vendor_id
, &id
) && id
) {
198 dev_mode
->dmFields
|= DM_PAPERSIZE
;
199 dev_mode
->dmPaperSize
= static_cast<short>(id
);
200 } else if (width
> 0 && height
> 0) {
201 dev_mode
->dmFields
|= DM_PAPERWIDTH
;
202 dev_mode
->dmPaperWidth
= width
;
203 dev_mode
->dmFields
|= DM_PAPERLENGTH
;
204 dev_mode
->dmPaperLength
= height
;
208 // Update data using DocumentProperties.
209 if (show_system_dialog
) {
210 PrintingContext::Result result
= PrintingContext::FAILED
;
211 AskUserForSettings(0, false, 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 defined(USE_AURA)
366 window
= view
->GetHost()->GetAcceleratedWidget();
368 if (view
&& IsWindow(view
)) {
369 window
= GetAncestor(view
, GA_ROOTOWNER
);
373 // TODO(maruel): crbug.com/1214347 Get the right browser window instead.
374 return GetDesktopWindow();
379 scoped_ptr
<DEVMODE
, base::FreeDeleter
> PrintingContextWin::ShowPrintDialog(
381 gfx::NativeView parent_view
,
383 // Note that this cannot use ui::BaseShellDialog as the print dialog is
384 // system modal: opening it from a background thread can cause Windows to
385 // get the wrong Z-order which will make the print dialog appear behind the
386 // browser frame (but still being modal) so neither the browser frame nor
387 // the print dialog will get any input. See http://crbug.com/342697
388 // http://crbug.com/180997 for details.
389 base::MessageLoop::ScopedNestableTaskAllower
allow(
390 base::MessageLoop::current());
392 bool canceled
= false;
393 scoped_ptr
<DEVMODE
, base::FreeDeleter
> result
=
394 PromptDevMode(printer
,
395 settings_
.device_name(),
397 GetRootWindow(parent_view
),
402 abort_printing_
= true;
405 return result
.Pass();
408 } // namespace printing