print_backend_win.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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. 4 5#include "printing/backend/print_backend.h" 6 7#include <objidl.h> 8#include <winspool.h> 9 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" 21 22namespace printing { 23 24namespace { 25 26HRESULT 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()); 35 } 36 return hr; 37} 38 39template <class T> 40void 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); 56} 57 58void 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; 64 65 struct PaperName { 66 wchar_t chars[kMaxPaperName]; 67 }; 68 69 DCHECK_EQ(sizeof(PaperName), sizeof(wchar_t) * kMaxPaperName); 70 71 // Paper 72 std::vector<PaperName> names; 73 GetDeviceCapabilityArray(printer, port, DC_PAPERNAMES, &names); 74 75 std::vector<POINT> sizes; 76 GetDeviceCapabilityArray(printer, port, DC_PAPERSIZE, &sizes); 77 78 std::vector<WORD> ids; 79 GetDeviceCapabilityArray(printer, port, DC_PAPERS, &ids); 80 81 DCHECK_EQ(ids.size(), sizes.size()); 82 DCHECK_EQ(names.size(), sizes.size()); 83 84 if (ids.size() != sizes.size()) 85 ids.clear(); 86 if (names.size() != sizes.size()) 87 names.clear(); 88 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); 98 } 99 if (!ids.empty()) 100 paper.vendor_id = base::UintToString(ids[i]); 101 caps->papers.push_back(paper); 102 } 103 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; 112 } 113 } 114 } 115 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); 121 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; 128 } 129 } 130} 131 132void 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); 138 139 for (size_t i = 0; i < dpis.size() ; ++i) 140 caps->dpis.push_back(gfx::Size(dpis[i].x, dpis[i].y)); 141 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); 148 } 149 } 150 } 151} 152 153} // namespace 154 155class PrintBackendWin : public PrintBackend { 156 public: 157 PrintBackendWin() {} 158 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; 171 172 protected: 173 virtual ~PrintBackendWin() {} 174}; 175 176bool 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; 192 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, &info)) { 201 info.is_default = (info.printer_name == default_printer); 202 printer_list->push_back(info); 203 } 204 } 205 return true; 206} 207 208std::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); 214} 215 216bool 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; 223 } 224 225 PrinterInfo5 info_5; 226 if (!info_5.Init(printer_handle)) 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)); 231 232 PrinterSemanticCapsAndDefaults caps; 233 234 scoped_ptr<DEVMODE, base::FreeDeleter> user_settings = 235 CreateDevMode(printer_handle, NULL); 236 if (user_settings) { 237 if (user_settings->dmFields & DM_COLOR) 238 caps.color_default = (user_settings->dmColor == DMCOLOR_COLOR); 239 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(); 253 } 254 } 255 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; 262 } 263 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; 270 271 caps.duplex_capable = 272 (DeviceCapabilities(name, port, DC_DUPLEX, NULL, NULL) == 1); 273 274 caps.collate_capable = 275 (DeviceCapabilities(name, port, DC_COLLATE, NULL, NULL) == 1); 276 277 caps.copies_capable = 278 (DeviceCapabilities(name, port, DC_COPIES, NULL, NULL) > 1); 279 280 LoadPaper(name, port, user_settings.get(), &caps); 281 LoadDpi(name, port, user_settings.get(), &caps); 282 283 *printer_info = caps; 284 return true; 285} 286 287bool 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; 294 } 295 if (!IsValidPrinter(printer_name)) { 296 return false; 297 } 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) { 308 base::win::ScopedBstr error; 309 hr = XPSModule::GetPrintCapabilities(provider, 310 NULL, 311 print_capabilities_stream, 312 error.Receive()); 313 DCHECK(SUCCEEDED(hr)); 314 if (FAILED(hr)) { 315 return false; 316 } 317 hr = StreamOnHGlobalToString(print_capabilities_stream.get(), 318 &printer_info->printer_capabilities); 319 DCHECK(SUCCEEDED(hr)); 320 printer_info->caps_mime_type = "text/xml"; 321 } 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)); 326 if (!devmode_out) 327 return false; 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)); 337 if (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"; 342 } 343 } 344 } 345 XPSModule::CloseProvider(provider); 346 } 347 return true; 348} 349 350// Gets the information about driver for a specific printer. 351std::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(); 356 } 357 return GetDriverInfo(printer); 358} 359 360bool PrintBackendWin::IsValidPrinter(const std::string& printer_name) { 361 ScopedPrinterHandle printer_handle; 362 return printer_handle.OpenPrinter(base::UTF8ToWide(printer_name).c_str()); 363} 364 365scoped_refptr<PrintBackend> PrintBackend::CreateInstance( 366 const base::DictionaryValue* print_backend_settings) { 367 return new PrintBackendWin; 368} 369 370} // namespace printing 371