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