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_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/win/scoped_bstr.h"
16 #include "base/win/scoped_comptr.h"
17 #include "base/win/scoped_hglobal.h"
18 #include "printing/backend/print_backend_consts.h"
19 #include "printing/backend/printing_info_win.h"
20 #include "printing/backend/win_helper.h"
26 HRESULT
StreamOnHGlobalToString(IStream
* stream
, std::string
* out
) {
30 HRESULT hr
= GetHGlobalFromStream(stream
, &hdata
);
33 base::win::ScopedHGlobal
<char*> locked_data(hdata
);
34 out
->assign(locked_data
.release(), locked_data
.Size());
40 void GetDeviceCapabilityArray(const wchar_t* printer
,
43 std::vector
<T
>* result
) {
44 int count
= DeviceCapabilities(printer
, port
, id
, NULL
, NULL
);
48 tmp
.resize(count
* 2);
49 count
= DeviceCapabilities(printer
, port
, id
,
50 reinterpret_cast<LPTSTR
>(tmp
.data()), NULL
);
53 CHECK_LE(count
, base::checked_cast
<int>(tmp
.size()));
58 void LoadPaper(const wchar_t* printer
,
60 const DEVMODE
* devmode
,
61 PrinterSemanticCapsAndDefaults
* caps
) {
62 static const size_t kToUm
= 100; // Windows uses 0.1mm.
63 static const size_t kMaxPaperName
= 64;
66 wchar_t chars
[kMaxPaperName
];
69 DCHECK_EQ(sizeof(PaperName
), sizeof(wchar_t) * kMaxPaperName
);
72 std::vector
<PaperName
> names
;
73 GetDeviceCapabilityArray(printer
, port
, DC_PAPERNAMES
, &names
);
75 std::vector
<POINT
> sizes
;
76 GetDeviceCapabilityArray(printer
, port
, DC_PAPERSIZE
, &sizes
);
78 std::vector
<WORD
> ids
;
79 GetDeviceCapabilityArray(printer
, port
, DC_PAPERS
, &ids
);
81 DCHECK_EQ(ids
.size(), sizes
.size());
82 DCHECK_EQ(names
.size(), sizes
.size());
84 if (ids
.size() != sizes
.size())
86 if (names
.size() != sizes
.size())
89 for (size_t i
= 0; i
< sizes
.size(); ++i
) {
90 PrinterSemanticCapsAndDefaults::Paper paper
;
91 paper
.size_um
.SetSize(sizes
[i
].x
* kToUm
, sizes
[i
].y
* kToUm
);
93 const wchar_t* name_start
= names
[i
].chars
;
94 base::string16
tmp_name(name_start
, kMaxPaperName
);
95 // Trim trailing zeros.
96 tmp_name
= tmp_name
.c_str();
97 paper
.display_name
= base::WideToUTF8(tmp_name
);
100 paper
.vendor_id
= base::UintToString(ids
[i
]);
101 caps
->papers
.push_back(paper
);
105 // Copy paper with the same ID as default paper.
106 if (devmode
->dmFields
& DM_PAPERSIZE
) {
107 for (size_t i
= 0; i
< ids
.size(); ++i
) {
108 if (ids
[i
] == devmode
->dmPaperSize
) {
109 DCHECK_EQ(ids
.size(), caps
->papers
.size());
110 caps
->default_paper
= caps
->papers
[i
];
116 gfx::Size default_size
;
117 if (devmode
->dmFields
& DM_PAPERWIDTH
)
118 default_size
.set_width(devmode
->dmPaperWidth
* kToUm
);
119 if (devmode
->dmFields
& DM_PAPERLENGTH
)
120 default_size
.set_height(devmode
->dmPaperLength
* kToUm
);
122 if (!default_size
.IsEmpty()) {
123 // Reset default paper if |dmPaperWidth| or |dmPaperLength| does not
124 // match default paper set by.
125 if (default_size
!= caps
->default_paper
.size_um
)
126 caps
->default_paper
= PrinterSemanticCapsAndDefaults::Paper();
127 caps
->default_paper
.size_um
= default_size
;
132 void LoadDpi(const wchar_t* printer
,
134 const DEVMODE
* devmode
,
135 PrinterSemanticCapsAndDefaults
* caps
) {
136 std::vector
<POINT
> dpis
;
137 GetDeviceCapabilityArray(printer
, port
, DC_ENUMRESOLUTIONS
, &dpis
);
139 for (size_t i
= 0; i
< dpis
.size() ; ++i
)
140 caps
->dpis
.push_back(gfx::Size(dpis
[i
].x
, dpis
[i
].y
));
143 if ((devmode
->dmFields
& DM_PRINTQUALITY
) && devmode
->dmPrintQuality
> 0) {
144 caps
->default_dpi
.SetSize(devmode
->dmPrintQuality
,
145 devmode
->dmPrintQuality
);
146 if (devmode
->dmFields
& DM_YRESOLUTION
) {
147 caps
->default_dpi
.set_height(devmode
->dmYResolution
);
155 class PrintBackendWin
: public PrintBackend
{
159 // PrintBackend implementation.
160 virtual bool EnumeratePrinters(PrinterList
* printer_list
) override
;
161 virtual std::string
GetDefaultPrinterName() override
;
162 virtual bool GetPrinterSemanticCapsAndDefaults(
163 const std::string
& printer_name
,
164 PrinterSemanticCapsAndDefaults
* printer_info
) override
;
165 virtual bool GetPrinterCapsAndDefaults(
166 const std::string
& printer_name
,
167 PrinterCapsAndDefaults
* printer_info
) override
;
168 virtual std::string
GetPrinterDriverInfo(
169 const std::string
& printer_name
) override
;
170 virtual bool IsValidPrinter(const std::string
& printer_name
) override
;
173 virtual ~PrintBackendWin() {}
176 bool PrintBackendWin::EnumeratePrinters(PrinterList
* printer_list
) {
177 DCHECK(printer_list
);
178 DWORD bytes_needed
= 0;
179 DWORD count_returned
= 0;
180 const DWORD kLevel
= 4;
181 BOOL ret
= EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
, NULL
,
182 kLevel
, NULL
, 0, &bytes_needed
, &count_returned
);
185 scoped_ptr
<BYTE
[]> printer_info_buffer(new BYTE
[bytes_needed
]);
186 ret
= EnumPrinters(PRINTER_ENUM_LOCAL
|PRINTER_ENUM_CONNECTIONS
, NULL
, kLevel
,
187 printer_info_buffer
.get(), bytes_needed
, &bytes_needed
,
193 std::string default_printer
= GetDefaultPrinterName();
194 PRINTER_INFO_4
* printer_info
=
195 reinterpret_cast<PRINTER_INFO_4
*>(printer_info_buffer
.get());
196 for (DWORD index
= 0; index
< count_returned
; index
++) {
197 ScopedPrinterHandle printer
;
198 PrinterBasicInfo info
;
199 if (printer
.OpenPrinter(printer_info
[index
].pPrinterName
) &&
200 InitBasicPrinterInfo(printer
.Get(), &info
)) {
201 info
.is_default
= (info
.printer_name
== default_printer
);
202 printer_list
->push_back(info
);
208 std::string
PrintBackendWin::GetDefaultPrinterName() {
209 DWORD size
= MAX_PATH
;
210 TCHAR default_printer_name
[MAX_PATH
];
211 if (!::GetDefaultPrinter(default_printer_name
, &size
))
212 return std::string();
213 return base::WideToUTF8(default_printer_name
);
216 bool PrintBackendWin::GetPrinterSemanticCapsAndDefaults(
217 const std::string
& printer_name
,
218 PrinterSemanticCapsAndDefaults
* printer_info
) {
219 ScopedPrinterHandle printer_handle
;
220 if (!printer_handle
.OpenPrinter(base::UTF8ToWide(printer_name
).c_str())) {
221 LOG(WARNING
) << "Failed to open printer, error = " << GetLastError();
226 if (!info_5
.Init(printer_handle
.Get()))
228 const wchar_t* name
= info_5
.get()->pPrinterName
;
229 const wchar_t* port
= info_5
.get()->pPortName
;
230 DCHECK_EQ(name
, base::UTF8ToUTF16(printer_name
));
232 PrinterSemanticCapsAndDefaults caps
;
234 scoped_ptr
<DEVMODE
, base::FreeDeleter
> user_settings
=
235 CreateDevMode(printer_handle
.Get(), NULL
);
237 if (user_settings
->dmFields
& DM_COLOR
)
238 caps
.color_default
= (user_settings
->dmColor
== DMCOLOR_COLOR
);
240 if (user_settings
->dmFields
& DM_DUPLEX
) {
241 switch (user_settings
->dmDuplex
) {
243 caps
.duplex_default
= SIMPLEX
;
246 caps
.duplex_default
= LONG_EDGE
;
248 case DMDUP_HORIZONTAL
:
249 caps
.duplex_default
= SHORT_EDGE
;
256 if (user_settings
->dmFields
& DM_COLLATE
)
257 caps
.collate_default
= (user_settings
->dmCollate
== DMCOLLATE_TRUE
);
259 LOG(WARNING
) << "Fallback to color/simplex mode.";
260 caps
.color_default
= caps
.color_changeable
;
261 caps
.duplex_default
= SIMPLEX
;
264 // Get printer capabilities. For more info see here:
265 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183552(v=vs.85).aspx
266 caps
.color_changeable
=
267 (DeviceCapabilities(name
, port
, DC_COLORDEVICE
, NULL
, NULL
) == 1);
268 caps
.color_model
= printing::COLOR
;
269 caps
.bw_model
= printing::GRAY
;
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
.Get(), 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
.Get());
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