blob: 01be277fe11f72dc23cb987fddaa2a5f30dcd6b2 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "printing/backend/print_backend.h"
#include <objidl.h>
#include <winspool.h>
#include "base/memory/scoped_ptr.h"
#include "base/string_piece.h"
#include "base/utf_string_conversions.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_hglobal.h"
#include "printing/backend/print_backend_consts.h"
#include "printing/backend/win_helper.h"
namespace {
HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) {
DCHECK(stream);
DCHECK(out);
HGLOBAL hdata = NULL;
HRESULT hr = GetHGlobalFromStream(stream, &hdata);
if (SUCCEEDED(hr)) {
DCHECK(hdata);
base::win::ScopedHGlobal<char> locked_data(hdata);
out->assign(locked_data.release(), locked_data.Size());
}
return hr;
}
} // namespace
namespace printing {
class PrintBackendWin : public PrintBackend {
public:
PrintBackendWin() {}
virtual ~PrintBackendWin() {}
virtual bool EnumeratePrinters(PrinterList* printer_list);
virtual std::string GetDefaultPrinterName();
virtual bool GetPrinterCapsAndDefaults(const std::string& printer_name,
PrinterCapsAndDefaults* printer_info);
virtual bool IsValidPrinter(const std::string& printer_name);
};
bool PrintBackendWin::EnumeratePrinters(PrinterList* printer_list) {
DCHECK(printer_list);
DWORD bytes_needed = 0;
DWORD count_returned = 0;
BOOL ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2,
NULL, 0, &bytes_needed, &count_returned);
if (!bytes_needed)
return false;
scoped_array<BYTE> printer_info_buffer(new BYTE[bytes_needed]);
ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, 2,
printer_info_buffer.get(), bytes_needed, &bytes_needed,
&count_returned);
DCHECK(ret);
if (!ret)
return false;
std::string default_printer = GetDefaultPrinterName();
PRINTER_INFO_2* printer_info =
reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get());
for (DWORD index = 0; index < count_returned; index++) {
PrinterBasicInfo info;
info.printer_name = WideToUTF8(printer_info[index].pPrinterName);
info.is_default = (info.printer_name == default_printer);
if (printer_info[index].pComment)
info.printer_description = WideToUTF8(printer_info[index].pComment);
info.printer_status = printer_info[index].Status;
if (printer_info[index].pLocation)
info.options[kLocationTagName] =
WideToUTF8(printer_info[index].pLocation);
if (printer_info[index].pDriverName)
info.options[kDriverNameTagName] =
WideToUTF8(printer_info[index].pDriverName);
printer_list->push_back(info);
}
return true;
}
std::string PrintBackendWin::GetDefaultPrinterName() {
DWORD size = MAX_PATH;
TCHAR default_printer_name[MAX_PATH];
if (!::GetDefaultPrinter(default_printer_name, &size))
return std::string();
return WideToUTF8(default_printer_name);
}
bool PrintBackendWin::GetPrinterCapsAndDefaults(
const std::string& printer_name,
PrinterCapsAndDefaults* printer_info) {
ScopedXPSInitializer xps_initializer;
if (!xps_initializer.initialized()) {
// TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
return false;
}
if (!IsValidPrinter(printer_name)) {
return false;
}
DCHECK(printer_info);
HPTPROVIDER provider = NULL;
std::wstring printer_name_wide = UTF8ToWide(printer_name);
HRESULT hr = XPSModule::OpenProvider(printer_name_wide, 1, &provider);
DCHECK(SUCCEEDED(hr));
if (provider) {
base::win::ScopedComPtr<IStream> print_capabilities_stream;
hr = CreateStreamOnHGlobal(NULL, TRUE,
print_capabilities_stream.Receive());
DCHECK(SUCCEEDED(hr));
if (print_capabilities_stream) {
base::win::ScopedBstr error;
hr = XPSModule::GetPrintCapabilities(provider,
NULL,
print_capabilities_stream,
error.Receive());
DCHECK(SUCCEEDED(hr));
if (FAILED(hr)) {
return false;
}
hr = StreamOnHGlobalToString(print_capabilities_stream.get(),
&printer_info->printer_capabilities);
DCHECK(SUCCEEDED(hr));
printer_info->caps_mime_type = "text/xml";
}
// TODO(sanjeevr): Add ScopedPrinterHandle
HANDLE printer_handle = NULL;
OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
NULL);
DCHECK(printer_handle);
if (printer_handle) {
LONG devmode_size = DocumentProperties(
NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
NULL, NULL, 0);
if (devmode_size <= 0)
return false;
scoped_array<BYTE> devmode_out_buffer(new BYTE[devmode_size]);
DEVMODE* devmode_out =
reinterpret_cast<DEVMODE*>(devmode_out_buffer.get());
DocumentProperties(
NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
devmode_out, NULL, DM_OUT_BUFFER);
base::win::ScopedComPtr<IStream> printer_defaults_stream;
hr = CreateStreamOnHGlobal(NULL, TRUE,
printer_defaults_stream.Receive());
DCHECK(SUCCEEDED(hr));
if (printer_defaults_stream) {
hr = XPSModule::ConvertDevModeToPrintTicket(provider,
devmode_size,
devmode_out,
kPTJobScope,
printer_defaults_stream);
DCHECK(SUCCEEDED(hr));
if (SUCCEEDED(hr)) {
hr = StreamOnHGlobalToString(printer_defaults_stream.get(),
&printer_info->printer_defaults);
DCHECK(SUCCEEDED(hr));
printer_info->defaults_mime_type = "text/xml";
}
}
ClosePrinter(printer_handle);
}
XPSModule::CloseProvider(provider);
}
return true;
}
bool PrintBackendWin::IsValidPrinter(const std::string& printer_name) {
std::wstring printer_name_wide = UTF8ToWide(printer_name);
HANDLE printer_handle = NULL;
OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
NULL);
bool ret = false;
if (printer_handle) {
ret = true;
ClosePrinter(printer_handle);
}
return ret;
}
scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
const base::DictionaryValue* print_backend_settings) {
return new PrintBackendWin;
}
} // namespace printing