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.Get(), &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.Get()))
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.Get(), 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.Get(), 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.Get());
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