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