print_backend_win.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/string_piece.h" 12#include "base/utf_string_conversions.h" 13#include "base/win/scoped_bstr.h" 14#include "base/win/scoped_comptr.h" 15#include "base/win/scoped_hglobal.h" 16#include "printing/backend/print_backend_consts.h" 17#include "printing/backend/win_helper.h" 18 19namespace { 20 21// This class is designed to work with PRINTER_INFO_X structures 22// and calls GetPrinter internally with correctly allocated buffer. 23template <typename T> 24class PrinterInfo { 25 public: 26 bool GetPrinterInfo(HANDLE printer, int level) { 27 DWORD buf_size = 0; 28 GetPrinter(printer, level, NULL, 0, &buf_size); 29 if (buf_size == 0) 30 return false; 31 buffer_.reset(new uint8[buf_size]); 32 memset(buffer_.get(), 0, buf_size); 33 return !!GetPrinter(printer, level, buffer_.get(), buf_size, &buf_size); 34 } 35 36 const T* get() const { 37 return reinterpret_cast<T*>(buffer_.get()); 38 } 39 40 private: 41 scoped_array<uint8> buffer_; 42}; 43 44HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) { 45 DCHECK(stream); 46 DCHECK(out); 47 HGLOBAL hdata = NULL; 48 HRESULT hr = GetHGlobalFromStream(stream, &hdata); 49 if (SUCCEEDED(hr)) { 50 DCHECK(hdata); 51 base::win::ScopedHGlobal<char> locked_data(hdata); 52 out->assign(locked_data.release(), locked_data.Size()); 53 } 54 return hr; 55} 56 57} // namespace 58 59namespace printing { 60 61class PrintBackendWin : public PrintBackend { 62 public: 63 PrintBackendWin() {} 64 65 // PrintBackend implementation. 66 virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE; 67 virtual std::string GetDefaultPrinterName() OVERRIDE; 68 virtual bool GetPrinterSemanticCapsAndDefaults( 69 const std::string& printer_name, 70 PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE; 71 virtual bool GetPrinterCapsAndDefaults( 72 const std::string& printer_name, 73 PrinterCapsAndDefaults* printer_info) OVERRIDE; 74 virtual std::string GetPrinterDriverInfo( 75 const std::string& printer_name) OVERRIDE; 76 virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE; 77 78 protected: 79 virtual ~PrintBackendWin() {} 80}; 81 82bool PrintBackendWin::EnumeratePrinters(PrinterList* printer_list) { 83 DCHECK(printer_list); 84 DWORD bytes_needed = 0; 85 DWORD count_returned = 0; 86 const DWORD kLevel = 4; 87 BOOL ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 88 kLevel, NULL, 0, &bytes_needed, &count_returned); 89 if (!bytes_needed) 90 return false; 91 scoped_array<BYTE> printer_info_buffer(new BYTE[bytes_needed]); 92 ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, kLevel, 93 printer_info_buffer.get(), bytes_needed, &bytes_needed, 94 &count_returned); 95 DCHECK(ret); 96 if (!ret) 97 return false; 98 99 std::string default_printer = GetDefaultPrinterName(); 100 PRINTER_INFO_4* printer_info = 101 reinterpret_cast<PRINTER_INFO_4*>(printer_info_buffer.get()); 102 for (DWORD index = 0; index < count_returned; index++) { 103 ScopedPrinterHandle printer; 104 OpenPrinter(printer_info[index].pPrinterName, printer.Receive(), NULL); 105 PrinterBasicInfo info; 106 if (InitBasicPrinterInfo(printer, &info)) { 107 info.is_default = (info.printer_name == default_printer); 108 printer_list->push_back(info); 109 } 110 } 111 return true; 112} 113 114std::string PrintBackendWin::GetDefaultPrinterName() { 115 DWORD size = MAX_PATH; 116 TCHAR default_printer_name[MAX_PATH]; 117 if (!::GetDefaultPrinter(default_printer_name, &size)) 118 return std::string(); 119 return WideToUTF8(default_printer_name); 120} 121 122bool PrintBackendWin::GetPrinterSemanticCapsAndDefaults( 123 const std::string& printer_name, 124 PrinterSemanticCapsAndDefaults* printer_info) { 125 ScopedPrinterHandle printer_handle; 126 OpenPrinter(const_cast<LPTSTR>(UTF8ToWide(printer_name).c_str()), 127 printer_handle.Receive(), NULL); 128 DCHECK(printer_handle); 129 if (!printer_handle.IsValid()) 130 return false; 131 132 PrinterInfo<PRINTER_INFO_5> info_5; 133 if (!info_5.GetPrinterInfo(printer_handle, 5)) 134 return false; 135 136 // Get printer capabilities. For more info see here: 137 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183552(v=vs.85).aspx 138 bool color_supported = (DeviceCapabilities(info_5.get()->pPrinterName, 139 info_5.get()->pPortName, 140 DC_COLORDEVICE, 141 NULL, 142 NULL) == 1); 143 144 bool duplex_supported = (DeviceCapabilities(info_5.get()->pPrinterName, 145 info_5.get()->pPortName, 146 DC_DUPLEX, 147 NULL, 148 NULL) == 1); 149 150 // PRINTER_INFO_9 retrieves current user settings. 151 PrinterInfo<PRINTER_INFO_9> info_9; 152 if (!info_9.GetPrinterInfo(printer_handle, 9)) 153 return false; 154 DEVMODE* devmode = info_9.get()->pDevMode; 155 156 // Sometimes user settings are not available (have not been setted up yet). 157 // Use printer default settings (PRINTER_INFO_8) in this case. 158 PrinterInfo<PRINTER_INFO_8> info_8; 159 if (!devmode) { 160 if (info_8.GetPrinterInfo(printer_handle, 8)) 161 devmode = info_8.get()->pDevMode; 162 } 163 if (!devmode) 164 return false; 165 166 PrinterSemanticCapsAndDefaults caps; 167 caps.color_capable = color_supported; 168 if ((devmode->dmFields & DM_COLOR) == DM_COLOR) 169 caps.color_default = (devmode->dmColor == DMCOLOR_COLOR); 170 171 caps.duplex_capable = duplex_supported; 172 if ((devmode->dmFields & DM_DUPLEX) == DM_DUPLEX) { 173 switch (devmode->dmDuplex) { 174 case DMDUP_SIMPLEX: 175 caps.duplex_default = SIMPLEX; 176 break; 177 case DMDUP_VERTICAL: 178 caps.duplex_default = LONG_EDGE; 179 break; 180 case DMDUP_HORIZONTAL: 181 caps.duplex_default = SHORT_EDGE; 182 break; 183 default: 184 NOTREACHED(); 185 } 186 } 187 188 *printer_info = caps; 189 return true; 190} 191 192bool PrintBackendWin::GetPrinterCapsAndDefaults( 193 const std::string& printer_name, 194 PrinterCapsAndDefaults* printer_info) { 195 ScopedXPSInitializer xps_initializer; 196 if (!xps_initializer.initialized()) { 197 // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) 198 return false; 199 } 200 if (!IsValidPrinter(printer_name)) { 201 return false; 202 } 203 DCHECK(printer_info); 204 HPTPROVIDER provider = NULL; 205 std::wstring printer_name_wide = UTF8ToWide(printer_name); 206 HRESULT hr = XPSModule::OpenProvider(printer_name_wide, 1, &provider); 207 if (provider) { 208 base::win::ScopedComPtr<IStream> print_capabilities_stream; 209 hr = CreateStreamOnHGlobal(NULL, TRUE, 210 print_capabilities_stream.Receive()); 211 DCHECK(SUCCEEDED(hr)); 212 if (print_capabilities_stream) { 213 base::win::ScopedBstr error; 214 hr = XPSModule::GetPrintCapabilities(provider, 215 NULL, 216 print_capabilities_stream, 217 error.Receive()); 218 DCHECK(SUCCEEDED(hr)); 219 if (FAILED(hr)) { 220 return false; 221 } 222 hr = StreamOnHGlobalToString(print_capabilities_stream.get(), 223 &printer_info->printer_capabilities); 224 DCHECK(SUCCEEDED(hr)); 225 printer_info->caps_mime_type = "text/xml"; 226 } 227 ScopedPrinterHandle printer_handle; 228 OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), 229 printer_handle.Receive(), NULL); 230 DCHECK(printer_handle); 231 if (printer_handle.IsValid()) { 232 LONG devmode_size = DocumentProperties( 233 NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()), 234 NULL, NULL, 0); 235 if (devmode_size <= 0) 236 return false; 237 scoped_array<BYTE> devmode_out_buffer(new BYTE[devmode_size]); 238 DEVMODE* devmode_out = 239 reinterpret_cast<DEVMODE*>(devmode_out_buffer.get()); 240 DocumentProperties( 241 NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()), 242 devmode_out, NULL, DM_OUT_BUFFER); 243 base::win::ScopedComPtr<IStream> printer_defaults_stream; 244 hr = CreateStreamOnHGlobal(NULL, TRUE, 245 printer_defaults_stream.Receive()); 246 DCHECK(SUCCEEDED(hr)); 247 if (printer_defaults_stream) { 248 hr = XPSModule::ConvertDevModeToPrintTicket(provider, 249 devmode_size, 250 devmode_out, 251 kPTJobScope, 252 printer_defaults_stream); 253 DCHECK(SUCCEEDED(hr)); 254 if (SUCCEEDED(hr)) { 255 hr = StreamOnHGlobalToString(printer_defaults_stream.get(), 256 &printer_info->printer_defaults); 257 DCHECK(SUCCEEDED(hr)); 258 printer_info->defaults_mime_type = "text/xml"; 259 } 260 } 261 } 262 XPSModule::CloseProvider(provider); 263 } 264 return true; 265} 266 267// Gets the information about driver for a specific printer. 268std::string PrintBackendWin::GetPrinterDriverInfo( 269 const std::string& printer_name) { 270 ScopedPrinterHandle printer; 271 if (!::OpenPrinter(const_cast<LPTSTR>(UTF8ToWide(printer_name).c_str()), 272 printer.Receive(), NULL)) { 273 return std::string(); 274 } 275 return GetDriverInfo(printer); 276} 277 278bool PrintBackendWin::IsValidPrinter(const std::string& printer_name) { 279 ScopedPrinterHandle printer_handle; 280 OpenPrinter(const_cast<LPTSTR>(UTF8ToWide(printer_name).c_str()), 281 printer_handle.Receive(), NULL); 282 return printer_handle.IsValid(); 283} 284 285scoped_refptr<PrintBackend> PrintBackend::CreateInstance( 286 const base::DictionaryValue* print_backend_settings) { 287 return new PrintBackendWin; 288} 289 290} // namespace printing 291