Home | History | Annotate | Download | only in backend
      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 
     22 namespace printing {
     23 
     24 namespace {
     25 
     26 HRESULT 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 
     39 template <class T>
     40 void 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 
     58 void 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 
    132 void 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 
    155 class 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 
    176 bool 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 
    208 std::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 
    216 bool 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 
    287 bool 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.
    351 std::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 
    360 bool PrintBackendWin::IsValidPrinter(const std::string& printer_name) {
    361   ScopedPrinterHandle printer_handle;
    362   return printer_handle.OpenPrinter(base::UTF8ToWide(printer_name).c_str());
    363 }
    364 
    365 scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
    366     const base::DictionaryValue* print_backend_settings) {
    367   return new PrintBackendWin;
    368 }
    369 
    370 }  // namespace printing
    371