Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / printing / backend / print_backend_win.cc
blobfa697b47491832171692329ddd56bc7b51c5aea3
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"
7 #include <objidl.h>
8 #include <winspool.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"
22 namespace printing {
24 namespace {
26 HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) {
27 DCHECK(stream);
28 DCHECK(out);
29 HGLOBAL hdata = NULL;
30 HRESULT hr = GetHGlobalFromStream(stream, &hdata);
31 if (SUCCEEDED(hr)) {
32 DCHECK(hdata);
33 base::win::ScopedHGlobal<char*> locked_data(hdata);
34 out->assign(locked_data.release(), locked_data.Size());
36 return hr;
39 template <class T>
40 void GetDeviceCapabilityArray(const wchar_t* printer,
41 const wchar_t* port,
42 WORD id,
43 std::vector<T>* result) {
44 int count = DeviceCapabilities(printer, port, id, NULL, NULL);
45 if (count <= 0)
46 return;
47 std::vector<T> tmp;
48 tmp.resize(count * 2);
49 count = DeviceCapabilities(printer, port, id,
50 reinterpret_cast<LPTSTR>(tmp.data()), NULL);
51 if (count <= 0)
52 return;
53 CHECK_LE(count, base::checked_cast<int>(tmp.size()));
54 tmp.resize(count);
55 result->swap(tmp);
58 void LoadPaper(const wchar_t* printer,
59 const wchar_t* port,
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;
65 struct PaperName {
66 wchar_t chars[kMaxPaperName];
69 DCHECK_EQ(sizeof(PaperName), sizeof(wchar_t) * kMaxPaperName);
71 // Paper
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())
85 ids.clear();
86 if (names.size() != sizes.size())
87 names.clear();
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);
92 if (!names.empty()) {
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);
99 if (!ids.empty())
100 paper.vendor_id = base::UintToString(ids[i]);
101 caps->papers.push_back(paper);
104 if (devmode) {
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];
111 break;
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,
133 const wchar_t* port,
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));
142 if (devmode) {
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);
153 } // namespace
155 class PrintBackendWin : public PrintBackend {
156 public:
157 PrintBackendWin() {}
159 // PrintBackend implementation.
160 bool EnumeratePrinters(PrinterList* printer_list) override;
161 std::string GetDefaultPrinterName() override;
162 bool GetPrinterSemanticCapsAndDefaults(
163 const std::string& printer_name,
164 PrinterSemanticCapsAndDefaults* printer_info) override;
165 bool GetPrinterCapsAndDefaults(
166 const std::string& printer_name,
167 PrinterCapsAndDefaults* printer_info) override;
168 std::string GetPrinterDriverInfo(
169 const std::string& printer_name) override;
170 bool IsValidPrinter(const std::string& printer_name) override;
172 protected:
173 ~PrintBackendWin() override {}
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);
183 if (!bytes_needed)
184 return false;
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,
188 &count_returned);
189 DCHECK(ret);
190 if (!ret)
191 return false;
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);
205 return true;
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();
222 return false;
225 PrinterInfo5 info_5;
226 if (!info_5.Init(printer_handle.Get()))
227 return false;
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);
236 if (user_settings) {
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) {
242 case DMDUP_SIMPLEX:
243 caps.duplex_default = SIMPLEX;
244 break;
245 case DMDUP_VERTICAL:
246 caps.duplex_default = LONG_EDGE;
247 break;
248 case DMDUP_HORIZONTAL:
249 caps.duplex_default = SHORT_EDGE;
250 break;
251 default:
252 NOTREACHED();
256 if (user_settings->dmFields & DM_COLLATE)
257 caps.collate_default = (user_settings->dmCollate == DMCOLLATE_TRUE);
258 } else {
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;
284 return true;
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)
293 return false;
295 if (!IsValidPrinter(printer_name)) {
296 return false;
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);
302 if (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.get()) {
308 base::win::ScopedBstr error;
309 hr = XPSModule::GetPrintCapabilities(
310 provider, NULL, print_capabilities_stream.get(), error.Receive());
311 DCHECK(SUCCEEDED(hr));
312 if (FAILED(hr)) {
313 return false;
315 hr = StreamOnHGlobalToString(print_capabilities_stream.get(),
316 &printer_info->printer_capabilities);
317 DCHECK(SUCCEEDED(hr));
318 printer_info->caps_mime_type = "text/xml";
320 ScopedPrinterHandle printer_handle;
321 if (printer_handle.OpenPrinter(printer_name_wide.c_str())) {
322 scoped_ptr<DEVMODE, base::FreeDeleter> devmode_out(
323 CreateDevMode(printer_handle.Get(), NULL));
324 if (!devmode_out)
325 return false;
326 base::win::ScopedComPtr<IStream> printer_defaults_stream;
327 hr = CreateStreamOnHGlobal(NULL, TRUE,
328 printer_defaults_stream.Receive());
329 DCHECK(SUCCEEDED(hr));
330 if (printer_defaults_stream.get()) {
331 DWORD dm_size = devmode_out->dmSize + devmode_out->dmDriverExtra;
332 hr = XPSModule::ConvertDevModeToPrintTicket(
333 provider, dm_size, devmode_out.get(), kPTJobScope,
334 printer_defaults_stream.get());
335 DCHECK(SUCCEEDED(hr));
336 if (SUCCEEDED(hr)) {
337 hr = StreamOnHGlobalToString(printer_defaults_stream.get(),
338 &printer_info->printer_defaults);
339 DCHECK(SUCCEEDED(hr));
340 printer_info->defaults_mime_type = "text/xml";
344 XPSModule::CloseProvider(provider);
346 return true;
349 // Gets the information about driver for a specific printer.
350 std::string PrintBackendWin::GetPrinterDriverInfo(
351 const std::string& printer_name) {
352 ScopedPrinterHandle printer;
353 if (!printer.OpenPrinter(base::UTF8ToWide(printer_name).c_str())) {
354 return std::string();
356 return GetDriverInfo(printer.Get());
359 bool PrintBackendWin::IsValidPrinter(const std::string& printer_name) {
360 ScopedPrinterHandle printer_handle;
361 return printer_handle.OpenPrinter(base::UTF8ToWide(printer_name).c_str());
364 scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
365 const base::DictionaryValue* print_backend_settings) {
366 return new PrintBackendWin;
369 } // namespace printing