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/strings/string_piece.h" 12#include "base/strings/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/printing_info_win.h" 18#include "printing/backend/win_helper.h" 19 20 21namespace { 22 23HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) { 24 DCHECK(stream); 25 DCHECK(out); 26 HGLOBAL hdata = NULL; 27 HRESULT hr = GetHGlobalFromStream(stream, &hdata); 28 if (SUCCEEDED(hr)) { 29 DCHECK(hdata); 30 base::win::ScopedHGlobal<char> locked_data(hdata); 31 out->assign(locked_data.release(), locked_data.Size()); 32 } 33 return hr; 34} 35 36} // namespace 37 38namespace printing { 39 40class PrintBackendWin : public PrintBackend { 41 public: 42 PrintBackendWin() {} 43 44 // PrintBackend implementation. 45 virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE; 46 virtual std::string GetDefaultPrinterName() OVERRIDE; 47 virtual bool GetPrinterSemanticCapsAndDefaults( 48 const std::string& printer_name, 49 PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE; 50 virtual bool GetPrinterCapsAndDefaults( 51 const std::string& printer_name, 52 PrinterCapsAndDefaults* printer_info) OVERRIDE; 53 virtual std::string GetPrinterDriverInfo( 54 const std::string& printer_name) OVERRIDE; 55 virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE; 56 57 protected: 58 virtual ~PrintBackendWin() {} 59}; 60 61bool PrintBackendWin::EnumeratePrinters(PrinterList* printer_list) { 62 DCHECK(printer_list); 63 DWORD bytes_needed = 0; 64 DWORD count_returned = 0; 65 const DWORD kLevel = 4; 66 BOOL ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 67 kLevel, NULL, 0, &bytes_needed, &count_returned); 68 if (!bytes_needed) 69 return false; 70 scoped_ptr<BYTE[]> printer_info_buffer(new BYTE[bytes_needed]); 71 ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, kLevel, 72 printer_info_buffer.get(), bytes_needed, &bytes_needed, 73 &count_returned); 74 DCHECK(ret); 75 if (!ret) 76 return false; 77 78 std::string default_printer = GetDefaultPrinterName(); 79 PRINTER_INFO_4* printer_info = 80 reinterpret_cast<PRINTER_INFO_4*>(printer_info_buffer.get()); 81 for (DWORD index = 0; index < count_returned; index++) { 82 ScopedPrinterHandle printer; 83 PrinterBasicInfo info; 84 if (printer.OpenPrinter(printer_info[index].pPrinterName) && 85 InitBasicPrinterInfo(printer, &info)) { 86 info.is_default = (info.printer_name == default_printer); 87 printer_list->push_back(info); 88 } 89 } 90 return true; 91} 92 93std::string PrintBackendWin::GetDefaultPrinterName() { 94 DWORD size = MAX_PATH; 95 TCHAR default_printer_name[MAX_PATH]; 96 if (!::GetDefaultPrinter(default_printer_name, &size)) 97 return std::string(); 98 return WideToUTF8(default_printer_name); 99} 100 101bool PrintBackendWin::GetPrinterSemanticCapsAndDefaults( 102 const std::string& printer_name, 103 PrinterSemanticCapsAndDefaults* printer_info) { 104 ScopedPrinterHandle printer_handle; 105 if (!printer_handle.OpenPrinter(UTF8ToWide(printer_name).c_str())) { 106 LOG(WARNING) << "Failed to open printer, error = " << GetLastError(); 107 return false; 108 } 109 110 PrinterInfo5 info_5; 111 if (!info_5.Init(printer_handle)) { 112 return false; 113 } 114 DCHECK_EQ(info_5.get()->pPrinterName, UTF8ToUTF16(printer_name)); 115 116 PrinterSemanticCapsAndDefaults caps; 117 118 // Get printer capabilities. For more info see here: 119 // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183552(v=vs.85).aspx 120 caps.color_changeable = (::DeviceCapabilities(info_5.get()->pPrinterName, 121 info_5.get()->pPortName, 122 DC_COLORDEVICE, 123 NULL, 124 NULL) == 1); 125 126 caps.duplex_capable = (::DeviceCapabilities(info_5.get()->pPrinterName, 127 info_5.get()->pPortName, 128 DC_DUPLEX, 129 NULL, 130 NULL) == 1); 131 132 UserDefaultDevMode user_settings; 133 134 if (user_settings.Init(printer_handle)) { 135 if ((user_settings.get()->dmFields & DM_COLOR) == DM_COLOR) 136 caps.color_default = (user_settings.get()->dmColor == DMCOLOR_COLOR); 137 138 if ((user_settings.get()->dmFields & DM_DUPLEX) == DM_DUPLEX) { 139 switch (user_settings.get()->dmDuplex) { 140 case DMDUP_SIMPLEX: 141 caps.duplex_default = SIMPLEX; 142 break; 143 case DMDUP_VERTICAL: 144 caps.duplex_default = LONG_EDGE; 145 break; 146 case DMDUP_HORIZONTAL: 147 caps.duplex_default = SHORT_EDGE; 148 break; 149 default: 150 NOTREACHED(); 151 } 152 } 153 } else { 154 LOG(WARNING) << "Fallback to color/simplex mode."; 155 caps.color_default = caps.color_changeable; 156 caps.duplex_default = SIMPLEX; 157 } 158 159 *printer_info = caps; 160 return true; 161} 162 163bool PrintBackendWin::GetPrinterCapsAndDefaults( 164 const std::string& printer_name, 165 PrinterCapsAndDefaults* printer_info) { 166 ScopedXPSInitializer xps_initializer; 167 if (!xps_initializer.initialized()) { 168 // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll) 169 return false; 170 } 171 if (!IsValidPrinter(printer_name)) { 172 return false; 173 } 174 DCHECK(printer_info); 175 HPTPROVIDER provider = NULL; 176 std::wstring printer_name_wide = UTF8ToWide(printer_name); 177 HRESULT hr = XPSModule::OpenProvider(printer_name_wide, 1, &provider); 178 if (provider) { 179 base::win::ScopedComPtr<IStream> print_capabilities_stream; 180 hr = CreateStreamOnHGlobal(NULL, TRUE, 181 print_capabilities_stream.Receive()); 182 DCHECK(SUCCEEDED(hr)); 183 if (print_capabilities_stream) { 184 base::win::ScopedBstr error; 185 hr = XPSModule::GetPrintCapabilities(provider, 186 NULL, 187 print_capabilities_stream, 188 error.Receive()); 189 DCHECK(SUCCEEDED(hr)); 190 if (FAILED(hr)) { 191 return false; 192 } 193 hr = StreamOnHGlobalToString(print_capabilities_stream.get(), 194 &printer_info->printer_capabilities); 195 DCHECK(SUCCEEDED(hr)); 196 printer_info->caps_mime_type = "text/xml"; 197 } 198 ScopedPrinterHandle printer_handle; 199 if (printer_handle.OpenPrinter(printer_name_wide.c_str())) { 200 LONG devmode_size = DocumentProperties( 201 NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()), 202 NULL, NULL, 0); 203 if (devmode_size <= 0) 204 return false; 205 scoped_ptr<BYTE[]> devmode_out_buffer(new BYTE[devmode_size]); 206 DEVMODE* devmode_out = 207 reinterpret_cast<DEVMODE*>(devmode_out_buffer.get()); 208 DocumentProperties( 209 NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()), 210 devmode_out, NULL, DM_OUT_BUFFER); 211 base::win::ScopedComPtr<IStream> printer_defaults_stream; 212 hr = CreateStreamOnHGlobal(NULL, TRUE, 213 printer_defaults_stream.Receive()); 214 DCHECK(SUCCEEDED(hr)); 215 if (printer_defaults_stream) { 216 hr = XPSModule::ConvertDevModeToPrintTicket(provider, 217 devmode_size, 218 devmode_out, 219 kPTJobScope, 220 printer_defaults_stream); 221 DCHECK(SUCCEEDED(hr)); 222 if (SUCCEEDED(hr)) { 223 hr = StreamOnHGlobalToString(printer_defaults_stream.get(), 224 &printer_info->printer_defaults); 225 DCHECK(SUCCEEDED(hr)); 226 printer_info->defaults_mime_type = "text/xml"; 227 } 228 } 229 } 230 XPSModule::CloseProvider(provider); 231 } 232 return true; 233} 234 235// Gets the information about driver for a specific printer. 236std::string PrintBackendWin::GetPrinterDriverInfo( 237 const std::string& printer_name) { 238 ScopedPrinterHandle printer; 239 if (!printer.OpenPrinter(UTF8ToWide(printer_name).c_str())) { 240 return std::string(); 241 } 242 return GetDriverInfo(printer); 243} 244 245bool PrintBackendWin::IsValidPrinter(const std::string& printer_name) { 246 ScopedPrinterHandle printer_handle; 247 return printer_handle.OpenPrinter(UTF8ToWide(printer_name).c_str()); 248} 249 250scoped_refptr<PrintBackend> PrintBackend::CreateInstance( 251 const base::DictionaryValue* print_backend_settings) { 252 return new PrintBackendWin; 253} 254 255} // namespace printing 256