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/backend/print_backend.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/win/scoped_bstr.h"
15 #include "base/win/scoped_comptr.h"
16 #include "base/win/scoped_hglobal.h"
17 #include "printing/backend/print_backend_consts.h"
18 #include "printing/backend/printing_info_win.h"
19 #include "printing/backend/win_helper.h"
25 HRESULT
StreamOnHGlobalToString(IStream
* stream
, std::string
* out
) {
29 HRESULT hr
= GetHGlobalFromStream(stream
, &hdata
);
32 base::win::ScopedHGlobal
<char> locked_data(hdata
);
33 out
->assign(locked_data
.release(), locked_data
.Size());
39 void GetDeviceCapabilityArray(const wchar_t* printer
,
42 std::vector
<T
>* result
) {
43 int count
= DeviceCapabilities(printer
, port
, id
, NULL
, NULL
);
47 tmp
.resize(count
* 2);
48 count
= DeviceCapabilities(printer
, port
, id
,
49 reinterpret_cast<LPTSTR
>(tmp
.data()), NULL
);
52 CHECK_LE(count
, base::checked_cast
<int>(tmp
.size()));
57 void LoadPaper(const wchar_t* printer
,
59 const DEVMODE
* devmode
,
60 PrinterSemanticCapsAndDefaults
* caps
) {
61 static const size_t kToUm
= 100; // Windows uses 0.1mm.
62 static const size_t kMaxPaperName
= 64;
65 wchar_t chars
[kMaxPaperName
];
68 DCHECK_EQ(sizeof(PaperName
), sizeof(wchar_t) * kMaxPaperName
);
71 std::vector
<PaperName
> names
;
72 GetDeviceCapabilityArray(printer
, port
, DC_PAPERNAMES
, &names
);
74 std::vector
<POINT
> sizes
;
75 GetDeviceCapabilityArray(printer
, port
, DC_PAPERSIZE
, &sizes
);
77 std::vector
<WORD
> ids
;
78 GetDeviceCapabilityArray(printer
, port
, DC_PAPERS
, &ids
);
80 DCHECK_EQ(ids
.size(), sizes
.size());
81 DCHECK_EQ(names
.size(), sizes
.size());
83 if (ids
.size() != sizes
.size())
85 if (names
.size() != sizes
.size())
88 for (size_t i
= 0; i
< sizes
.size(); ++i
) {
89 PrinterSemanticCapsAndDefaults::Paper paper
;
90 paper
.size_um
.SetSize(sizes
[i
].x
* kToUm
, sizes
[i
].y
* kToUm
);
92 const wchar_t* name_start
= names
[i
].chars
;
93 base::string16
tmp_name(name_start
, kMaxPaperName
);
94 // Trim trailing zeros.
95 tmp_name
= tmp_name
.c_str();
96 paper
.name
= base::WideToUTF8(tmp_name
);
98 caps
->papers
.push_back(paper
);
102 short default_id
= 0;
103 gfx::Size default_size
;
105 if (devmode
->dmFields
& DM_PAPERSIZE
)
106 default_id
= devmode
->dmPaperSize
;
107 if (devmode
->dmFields
& DM_PAPERWIDTH
)
108 default_size
.set_width(devmode
->dmPaperWidth
* kToUm
);
109 if (devmode
->dmFields
& DM_PAPERLENGTH
)
110 default_size
.set_height(devmode
->dmPaperLength
* kToUm
);
112 if (default_size
.IsEmpty()) {
113 for (size_t i
= 0; i
< ids
.size(); ++i
) {
114 if (ids
[i
] == default_id
) {
115 PrinterSemanticCapsAndDefaults::Paper paper
;
116 paper
.size_um
.SetSize(sizes
[i
].x
* kToUm
, sizes
[i
].y
* kToUm
);
117 if (!names
.empty()) {
118 const wchar_t* name_start
= names
[i
].chars
;
119 base::string16
tmp_name(name_start
, kMaxPaperName
);
120 // Trim trailing zeros.
121 tmp_name
= tmp_name
.c_str();
122 paper
.name
= base::WideToUTF8(tmp_name
);
124 caps
->default_paper
= paper
;
129 caps
->default_paper
.size_um
= default_size
;
134 void LoadDpi(const wchar_t* printer
,
136 const DEVMODE
* devmode
,
137 PrinterSemanticCapsAndDefaults
* caps
) {
138 std::vector
<POINT
> dpis
;
139 GetDeviceCapabilityArray(printer
, port
, DC_ENUMRESOLUTIONS
, &dpis
);
141 for (size_t i
= 0; i
< dpis
.size() ; ++i
)
142 caps
->dpis
.push_back(gfx::Size(dpis
[i
].x
, dpis
[i
].y
));
145 if ((devmode
->dmFields
& DM_PRINTQUALITY
) && devmode
->dmPrintQuality
> 0) {
146 caps
->default_dpi
.SetSize(devmode
->dmPrintQuality
,
147 devmode
->dmPrintQuality
);
148 if (devmode
->dmFields
& DM_YRESOLUTION
) {
149 caps
->default_dpi
.set_height(devmode
->dmYResolution
);
157 class PrintBackendWin
: public PrintBackend
{
161 // PrintBackend implementation.
162 virtual bool EnumeratePrinters(PrinterList
* printer_list
) OVERRIDE
;
163 virtual std::string
GetDefaultPrinterName() OVERRIDE
;
164 virtual bool GetPrinterSemanticCapsAndDefaults(
165 const std::string
& printer_name
,
166 PrinterSemanticCapsAndDefaults
* printer_info
) OVERRIDE
;
167 virtual bool GetPrinterCapsAndDefaults(
168 const std::string
& printer_name
,
169 PrinterCapsAndDefaults
* printer_info
) OVERRIDE
;
170 virtual std::string
GetPrinterDriverInfo(
171 const std::string
& printer_name
) OVERRIDE
;
172 virtual bool IsValidPrinter(const std::string
& printer_name
) OVERRIDE
;
175 virtual ~PrintBackendWin() {}
178 bool PrintBackendWin::EnumeratePrinters(PrinterList
* printer_list
) {
179 DCHECK(printer_list
);
180 DWORD bytes_needed
= 0;
181 DWORD count_returned
= 0;
182 const DWORD kLevel
= 4;
183 BOOL ret
= EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
, NULL
,
184 kLevel
, NULL
, 0, &bytes_needed
, &count_returned
);
187 scoped_ptr
<BYTE
[]> printer_info_buffer(new BYTE
[bytes_needed
]);
188 ret
= EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
, NULL
, kLevel
,
189 printer_info_buffer
.get(), bytes_needed
, &bytes_needed
,
195 std::string default_printer
= GetDefaultPrinterName();
196 PRINTER_INFO_4
* printer_info
=
197 reinterpret_cast<PRINTER_INFO_4
*>(printer_info_buffer
.get());
198 for (DWORD index
= 0; index
< count_returned
; index
++) {
199 ScopedPrinterHandle printer
;
200 PrinterBasicInfo info
;
201 if (printer
.OpenPrinter(printer_info
[index
].pPrinterName
) &&
202 InitBasicPrinterInfo(printer
, &info
)) {
203 info
.is_default
= (info
.printer_name
== default_printer
);
204 printer_list
->push_back(info
);
210 std::string
PrintBackendWin::GetDefaultPrinterName() {
211 DWORD size
= MAX_PATH
;
212 TCHAR default_printer_name
[MAX_PATH
];
213 if (!::GetDefaultPrinter(default_printer_name
, &size
))
214 return std::string();
215 return base::WideToUTF8(default_printer_name
);
218 bool PrintBackendWin::GetPrinterSemanticCapsAndDefaults(
219 const std::string
& printer_name
,
220 PrinterSemanticCapsAndDefaults
* printer_info
) {
221 ScopedPrinterHandle printer_handle
;
222 if (!printer_handle
.OpenPrinter(base::UTF8ToWide(printer_name
).c_str())) {
223 LOG(WARNING
) << "Failed to open printer, error = " << GetLastError();
228 if (!info_5
.Init(printer_handle
))
230 const wchar_t* name
= info_5
.get()->pPrinterName
;
231 const wchar_t* port
= info_5
.get()->pPortName
;
232 DCHECK_EQ(name
, base::UTF8ToUTF16(printer_name
));
234 PrinterSemanticCapsAndDefaults caps
;
236 scoped_ptr
<DEVMODE
, base::FreeDeleter
> user_settings
=
237 CreateDevMode(printer_handle
, NULL
);
239 if (user_settings
->dmFields
& DM_COLOR
)
240 caps
.color_default
= (user_settings
->dmColor
== DMCOLOR_COLOR
);
242 if (user_settings
->dmFields
& DM_DUPLEX
) {
243 switch (user_settings
->dmDuplex
) {
245 caps
.duplex_default
= SIMPLEX
;
248 caps
.duplex_default
= LONG_EDGE
;
250 case DMDUP_HORIZONTAL
:
251 caps
.duplex_default
= SHORT_EDGE
;
258 if (user_settings
->dmFields
& DM_COLLATE
)
259 caps
.collate_default
= (user_settings
->dmCollate
== DMCOLLATE_TRUE
);
261 LOG(WARNING
) << "Fallback to color/simplex mode.";
262 caps
.color_default
= caps
.color_changeable
;
263 caps
.duplex_default
= SIMPLEX
;
266 // Get printer capabilities. For more info see here:
267 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183552(v=vs.85).aspx
268 caps
.color_changeable
=
269 (DeviceCapabilities(name
, port
, DC_COLORDEVICE
, NULL
, NULL
) == 1);
271 caps
.duplex_capable
=
272 (DeviceCapabilities(name
, port
, DC_DUPLEX
, NULL
, NULL
) == 1);
274 caps
.collate_capable
=
275 (DeviceCapabilities(name
, port
, DC_COLLATE
, NULL
, NULL
) == 1);
277 caps
.copies_capable
=
278 (DeviceCapabilities(name
, port
, DC_COPIES
, NULL
, NULL
) > 1);
280 LoadPaper(name
, port
, user_settings
.get(), &caps
);
281 LoadDpi(name
, port
, user_settings
.get(), &caps
);
283 *printer_info
= caps
;
287 bool PrintBackendWin::GetPrinterCapsAndDefaults(
288 const std::string
& printer_name
,
289 PrinterCapsAndDefaults
* printer_info
) {
290 ScopedXPSInitializer xps_initializer
;
291 if (!xps_initializer
.initialized()) {
292 // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
295 if (!IsValidPrinter(printer_name
)) {
298 DCHECK(printer_info
);
299 HPTPROVIDER provider
= NULL
;
300 std::wstring printer_name_wide
= base::UTF8ToWide(printer_name
);
301 HRESULT hr
= XPSModule::OpenProvider(printer_name_wide
, 1, &provider
);
303 base::win::ScopedComPtr
<IStream
> print_capabilities_stream
;
304 hr
= CreateStreamOnHGlobal(NULL
, TRUE
,
305 print_capabilities_stream
.Receive());
306 DCHECK(SUCCEEDED(hr
));
307 if (print_capabilities_stream
) {
308 base::win::ScopedBstr error
;
309 hr
= XPSModule::GetPrintCapabilities(provider
,
311 print_capabilities_stream
,
313 DCHECK(SUCCEEDED(hr
));
317 hr
= StreamOnHGlobalToString(print_capabilities_stream
.get(),
318 &printer_info
->printer_capabilities
);
319 DCHECK(SUCCEEDED(hr
));
320 printer_info
->caps_mime_type
= "text/xml";
322 ScopedPrinterHandle printer_handle
;
323 if (printer_handle
.OpenPrinter(printer_name_wide
.c_str())) {
324 scoped_ptr
<DEVMODE
, base::FreeDeleter
> devmode_out(
325 CreateDevMode(printer_handle
, NULL
));
328 base::win::ScopedComPtr
<IStream
> printer_defaults_stream
;
329 hr
= CreateStreamOnHGlobal(NULL
, TRUE
,
330 printer_defaults_stream
.Receive());
331 DCHECK(SUCCEEDED(hr
));
332 if (printer_defaults_stream
) {
333 DWORD dm_size
= devmode_out
->dmSize
+ devmode_out
->dmDriverExtra
;
334 hr
= XPSModule::ConvertDevModeToPrintTicket(provider
, dm_size
,
335 devmode_out
.get(), kPTJobScope
, printer_defaults_stream
);
336 DCHECK(SUCCEEDED(hr
));
338 hr
= StreamOnHGlobalToString(printer_defaults_stream
.get(),
339 &printer_info
->printer_defaults
);
340 DCHECK(SUCCEEDED(hr
));
341 printer_info
->defaults_mime_type
= "text/xml";
345 XPSModule::CloseProvider(provider
);
350 // Gets the information about driver for a specific printer.
351 std::string
PrintBackendWin::GetPrinterDriverInfo(
352 const std::string
& printer_name
) {
353 ScopedPrinterHandle printer
;
354 if (!printer
.OpenPrinter(base::UTF8ToWide(printer_name
).c_str())) {
355 return std::string();
357 return GetDriverInfo(printer
);
360 bool PrintBackendWin::IsValidPrinter(const std::string
& printer_name
) {
361 ScopedPrinterHandle printer_handle
;
362 return printer_handle
.OpenPrinter(base::UTF8ToWide(printer_name
).c_str());
365 scoped_refptr
<PrintBackend
> PrintBackend::CreateInstance(
366 const base::DictionaryValue
* print_backend_settings
) {
367 return new PrintBackendWin
;
370 } // namespace printing