blob: ce52f9959466bf8b88e6eb205b2c1602efa658e4 [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 "chrome/service/cloud_print/print_system.h"
#include <objidl.h>
#include <winspool.h>
#include <xpsprint.h>
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "base/win/object_watcher.h"
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_hdc.h"
#include "chrome/service/service_process.h"
#include "chrome/service/service_utility_process_host.h"
#include "grit/generated_resources.h"
#include "printing/backend/print_backend.h"
#include "printing/backend/print_backend_consts.h"
#include "printing/backend/win_helper.h"
#include "printing/emf_win.h"
#include "printing/page_range.h"
#include "printing/pdf_render_settings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/rect.h"
#pragma comment(lib, "rpcrt4.lib") // for UuidToString & Co.
namespace {
class DevMode {
public:
DevMode() : dm_(NULL) {}
~DevMode() { Free(); }
void Allocate(int size) {
Free();
dm_ = reinterpret_cast<DEVMODE*>(new char[size]);
}
void Free() {
if (dm_)
delete [] dm_;
dm_ = NULL;
}
DEVMODE* dm_;
private:
DISALLOW_COPY_AND_ASSIGN(DevMode);
};
inline HRESULT GetLastErrorHR() {
LONG error = GetLastError();
return HRESULT_FROM_WIN32(error);
}
HRESULT StreamFromPrintTicket(const std::string& print_ticket,
IStream** stream) {
DCHECK(stream);
HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
if (FAILED(hr)) {
return hr;
}
ULONG bytes_written = 0;
(*stream)->Write(print_ticket.c_str(), print_ticket.length(), &bytes_written);
DCHECK(bytes_written == print_ticket.length());
LARGE_INTEGER pos = {0};
ULARGE_INTEGER new_pos = {0};
(*stream)->Seek(pos, STREAM_SEEK_SET, &new_pos);
return S_OK;
}
HRESULT PrintTicketToDevMode(const std::string& printer_name,
const std::string& print_ticket,
DevMode* dev_mode) {
DCHECK(dev_mode);
printing::ScopedXPSInitializer xps_initializer;
if (!xps_initializer.initialized()) {
// TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
return E_FAIL;
}
base::win::ScopedComPtr<IStream> pt_stream;
HRESULT hr = StreamFromPrintTicket(print_ticket, pt_stream.Receive());
if (FAILED(hr))
return hr;
HPTPROVIDER provider = NULL;
hr = printing::XPSModule::OpenProvider(UTF8ToWide(printer_name),
1,
&provider);
if (SUCCEEDED(hr)) {
ULONG size = 0;
DEVMODE* dm = NULL;
hr = printing::XPSModule::ConvertPrintTicketToDevMode(provider,
pt_stream,
kUserDefaultDevmode,
kPTDocumentScope,
&size,
&dm,
NULL);
if (SUCCEEDED(hr)) {
dev_mode->Allocate(size);
memcpy(dev_mode->dm_, dm, size);
printing::XPSModule::ReleaseMemory(dm);
}
printing::XPSModule::CloseProvider(provider);
}
return hr;
}
} // namespace
namespace cloud_print {
class PrintSystemWatcherWin : public base::win::ObjectWatcher::Delegate {
public:
PrintSystemWatcherWin()
: printer_(NULL),
printer_change_(NULL),
delegate_(NULL),
did_signal_(false) {
}
~PrintSystemWatcherWin() {
Stop();
}
class Delegate {
public:
virtual ~Delegate() {}
virtual void OnPrinterAdded() = 0;
virtual void OnPrinterDeleted() = 0;
virtual void OnPrinterChanged() = 0;
virtual void OnJobChanged() = 0;
};
bool Start(const std::string& printer_name, Delegate* delegate) {
delegate_ = delegate;
// An empty printer name means watch the current server, we need to pass
// NULL to OpenPrinter.
LPTSTR printer_name_to_use = NULL;
std::wstring printer_name_wide;
if (!printer_name.empty()) {
printer_name_wide = UTF8ToWide(printer_name);
printer_name_to_use = const_cast<LPTSTR>(printer_name_wide.c_str());
}
bool ret = false;
OpenPrinter(printer_name_to_use, &printer_, NULL);
if (printer_) {
printer_change_ = FindFirstPrinterChangeNotification(
printer_, PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB, 0, NULL);
if (printer_change_) {
ret = watcher_.StartWatching(printer_change_, this);
}
}
if (!ret) {
Stop();
}
return ret;
}
bool Stop() {
watcher_.StopWatching();
if (printer_) {
ClosePrinter(printer_);
printer_ = NULL;
}
if (printer_change_) {
FindClosePrinterChangeNotification(printer_change_);
printer_change_ = NULL;
}
return true;
}
// base::ObjectWatcher::Delegate method
virtual void OnObjectSignaled(HANDLE object) {
DWORD change = 0;
FindNextPrinterChangeNotification(object, &change, NULL, NULL);
if (change != ((PRINTER_CHANGE_PRINTER|PRINTER_CHANGE_JOB) &
(~PRINTER_CHANGE_FAILED_CONNECTION_PRINTER))) {
// For printer connections, we get spurious change notifications with
// all flags set except PRINTER_CHANGE_FAILED_CONNECTION_PRINTER.
// Ignore these.
if (change & PRINTER_CHANGE_ADD_PRINTER) {
delegate_->OnPrinterAdded();
} else if (change & PRINTER_CHANGE_DELETE_PRINTER) {
delegate_->OnPrinterDeleted();
} else if (change & PRINTER_CHANGE_SET_PRINTER) {
delegate_->OnPrinterChanged();
}
if (change & PRINTER_CHANGE_JOB) {
delegate_->OnJobChanged();
}
}
watcher_.StartWatching(printer_change_, this);
}
bool GetCurrentPrinterInfo(printing::PrinterBasicInfo* printer_info) {
DCHECK(printer_info);
if (!printer_)
return false;
DWORD bytes_needed = 0;
bool ret = false;
GetPrinter(printer_, 2, NULL, 0, &bytes_needed);
if (0 != bytes_needed) {
scoped_array<BYTE> printer_info_buffer(new BYTE[bytes_needed]);
if (GetPrinter(printer_, 2, printer_info_buffer.get(),
bytes_needed, &bytes_needed)) {
PRINTER_INFO_2* printer_info_win =
reinterpret_cast<PRINTER_INFO_2*>(printer_info_buffer.get());
printer_info->printer_name = WideToUTF8(printer_info_win->pPrinterName);
if (printer_info_win->pComment)
printer_info->printer_description =
WideToUTF8(printer_info_win->pComment);
if (printer_info_win->pLocation)
printer_info->options[kLocationTagName] =
WideToUTF8(printer_info_win->pLocation);
if (printer_info_win->pDriverName)
printer_info->options[kDriverNameTagName] =
WideToUTF8(printer_info_win->pDriverName);
printer_info->printer_status = printer_info_win->Status;
ret = true;
}
}
return ret;
}
private:
base::win::ObjectWatcher watcher_;
HANDLE printer_; // The printer being watched
HANDLE printer_change_; // Returned by FindFirstPrinterChangeNotifier
Delegate* delegate_; // Delegate to notify
bool did_signal_; // DoneWaiting was called
};
// This typedef is to workaround the issue with certain versions of
// Visual Studio where it gets confused between multiple Delegate.
// In this case, some compilers get confused and inherit
// PrintSystemWin watchers from wrong Delegate, giving C2664 and C2259 errors.
typedef PrintSystemWatcherWin::Delegate PrintSystemWatcherWinDelegate;
class PrintSystemWin : public PrintSystem {
public:
PrintSystemWin();
// PrintSystem implementation.
virtual PrintSystemResult Init();
virtual PrintSystem::PrintSystemResult EnumeratePrinters(
printing::PrinterList* printer_list);
virtual void GetPrinterCapsAndDefaults(
const std::string& printer_name,
PrinterCapsAndDefaultsCallback* callback);
virtual bool IsValidPrinter(const std::string& printer_name);
virtual bool ValidatePrintTicket(const std::string& printer_name,
const std::string& print_ticket_data);
virtual bool GetJobDetails(const std::string& printer_name,
PlatformJobId job_id,
PrintJobDetails *job_details);
class PrintServerWatcherWin
: public PrintSystem::PrintServerWatcher,
public PrintSystemWatcherWinDelegate {
public:
PrintServerWatcherWin() : delegate_(NULL) {}
// PrintSystem::PrintServerWatcher interface
virtual bool StartWatching(
PrintSystem::PrintServerWatcher::Delegate* delegate) {
delegate_ = delegate;
return watcher_.Start(std::string(), this);
}
virtual bool StopWatching() {
bool ret = watcher_.Stop();
delegate_ = NULL;
return ret;
}
// PrintSystemWatcherWin::Delegate interface
virtual void OnPrinterAdded() {
delegate_->OnPrinterAdded();
}
virtual void OnPrinterDeleted() {
}
virtual void OnPrinterChanged() {
}
virtual void OnJobChanged() {
}
private:
PrintSystem::PrintServerWatcher::Delegate* delegate_;
PrintSystemWatcherWin watcher_;
DISALLOW_COPY_AND_ASSIGN(PrintServerWatcherWin);
};
class PrinterWatcherWin
: public PrintSystem::PrinterWatcher,
public PrintSystemWatcherWinDelegate {
public:
explicit PrinterWatcherWin(const std::string& printer_name)
: printer_name_(printer_name),
delegate_(NULL) {
}
// PrintSystem::PrinterWatcher interface
virtual bool StartWatching(
PrintSystem::PrinterWatcher::Delegate* delegate) {
delegate_ = delegate;
return watcher_.Start(printer_name_, this);
}
virtual bool StopWatching() {
bool ret = watcher_.Stop();
delegate_ = NULL;
return ret;
}
virtual bool GetCurrentPrinterInfo(
printing::PrinterBasicInfo* printer_info) {
return watcher_.GetCurrentPrinterInfo(printer_info);
}
// PrintSystemWatcherWin::Delegate interface
virtual void OnPrinterAdded() {
NOTREACHED();
}
virtual void OnPrinterDeleted() {
delegate_->OnPrinterDeleted();
}
virtual void OnPrinterChanged() {
delegate_->OnPrinterChanged();
}
virtual void OnJobChanged() {
delegate_->OnJobChanged();
}
private:
std::string printer_name_;
PrintSystem::PrinterWatcher::Delegate* delegate_;
PrintSystemWatcherWin watcher_;
DISALLOW_COPY_AND_ASSIGN(PrinterWatcherWin);
};
class JobSpoolerWin : public PrintSystem::JobSpooler {
public:
JobSpoolerWin() : core_(new Core) {}
// PrintSystem::JobSpooler implementation.
virtual bool Spool(const std::string& print_ticket,
const FilePath& print_data_file_path,
const std::string& print_data_mime_type,
const std::string& printer_name,
const std::string& job_title,
const std::vector<std::string>& tags,
JobSpooler::Delegate* delegate) {
// TODO(gene): add tags handling.
return core_->Spool(print_ticket, print_data_file_path,
print_data_mime_type, printer_name, job_title,
delegate);
}
private:
// We use a Core class because we want a separate RefCountedThreadSafe
// implementation for ServiceUtilityProcessHost::Client.
class Core : public ServiceUtilityProcessHost::Client,
public base::win::ObjectWatcher::Delegate {
public:
Core()
: last_page_printed_(-1),
job_id_(-1),
delegate_(NULL),
saved_dc_(0),
should_couninit_(false) {
}
~Core() {
}
bool Spool(const std::string& print_ticket,
const FilePath& print_data_file_path,
const std::string& print_data_mime_type,
const std::string& printer_name,
const std::string& job_title,
JobSpooler::Delegate* delegate) {
if (delegate_) {
// We are already in the process of printing.
NOTREACHED();
return false;
}
last_page_printed_ = -1;
// We only support PDF and XPS documents for now.
if (print_data_mime_type == "application/pdf") {
DevMode pt_dev_mode;
HRESULT hr = PrintTicketToDevMode(printer_name, print_ticket,
&pt_dev_mode);
if (FAILED(hr)) {
NOTREACHED();
return false;
}
HDC dc = CreateDC(L"WINSPOOL", UTF8ToWide(printer_name).c_str(),
NULL, pt_dev_mode.dm_);
if (!dc) {
NOTREACHED();
return false;
}
hr = E_FAIL;
DOCINFO di = {0};
di.cbSize = sizeof(DOCINFO);
std::wstring doc_name = UTF8ToWide(job_title);
di.lpszDocName = doc_name.c_str();
job_id_ = StartDoc(dc, &di);
if (job_id_ <= 0)
return false;
printer_dc_.Set(dc);
int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX);
int offset_x = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETX);
int offset_y = ::GetDeviceCaps(printer_dc_.Get(), PHYSICALOFFSETY);
saved_dc_ = SaveDC(printer_dc_.Get());
SetGraphicsMode(printer_dc_.Get(), GM_ADVANCED);
// Setup the matrix to translate and scale to the right place. Take in
// account the actual shrinking factor.
// Note that the printing output is relative to printable area of
// the page. That is 0,0 is offset by PHYSICALOFFSETX/Y from the page.
XFORM xform = {0};
xform.eDx = static_cast<float>(-offset_x);
xform.eDy = static_cast<float>(-offset_y);
xform.eM11 = xform.eM22 = static_cast<float>(printer_dpi) /
static_cast<float>(GetDeviceCaps(GetDC(NULL), LOGPIXELSX));
ModifyWorldTransform(printer_dc_.Get(), &xform, MWT_LEFTMULTIPLY);
print_data_file_path_ = print_data_file_path;
delegate_ = delegate;
RenderNextPDFPages();
} else if (print_data_mime_type == "application/vnd.ms-xpsdocument") {
bool ret = PrintXPSDocument(printer_name,
job_title,
print_data_file_path,
print_ticket);
if (ret)
delegate_ = delegate;
return ret;
} else {
NOTREACHED();
return false;
}
return true;
}
// ServiceUtilityProcessHost::Client implementation.
virtual void OnRenderPDFPagesToMetafileSucceeded(
const printing::Emf& metafile,
int highest_rendered_page_number) {
metafile.SafePlayback(printer_dc_.Get());
bool done_printing = (highest_rendered_page_number !=
last_page_printed_ + kPageCountPerBatch);
last_page_printed_ = highest_rendered_page_number;
if (done_printing)
PrintJobDone();
else
RenderNextPDFPages();
}
// base::win::ObjectWatcher::Delegate inplementation.
virtual void OnObjectSignaled(HANDLE object) {
DCHECK(xps_print_job_);
DCHECK(object == job_progress_event_.Get());
ResetEvent(job_progress_event_.Get());
if (!delegate_)
return;
XPS_JOB_STATUS job_status = {0};
xps_print_job_->GetJobStatus(&job_status);
bool done = false;
if ((job_status.completion == XPS_JOB_CANCELLED) ||
(job_status.completion == XPS_JOB_FAILED)) {
delegate_->OnJobSpoolFailed();
done = true;
} else if (job_status.jobId ||
(job_status.completion == XPS_JOB_COMPLETED)) {
// Note: In the case of the XPS document being printed to the
// Microsoft XPS Document Writer, it seems to skip spooling the job
// and goes to the completed state without ever assigning a job id.
delegate_->OnJobSpoolSucceeded(job_status.jobId);
done = true;
} else {
job_progress_watcher_.StopWatching();
job_progress_watcher_.StartWatching(job_progress_event_.Get(), this);
}
if (done && should_couninit_) {
CoUninitialize();
should_couninit_ = false;
}
}
virtual void OnRenderPDFPagesToMetafileFailed() {
PrintJobDone();
}
virtual void OnChildDied() {
PrintJobDone();
}
private:
void PrintJobDone() {
// If there is no delegate, then there is nothing pending to process.
if (!delegate_)
return;
RestoreDC(printer_dc_.Get(), saved_dc_);
EndDoc(printer_dc_.Get());
if (-1 == last_page_printed_) {
delegate_->OnJobSpoolFailed();
} else {
delegate_->OnJobSpoolSucceeded(job_id_);
}
delegate_ = NULL;
}
void RenderNextPDFPages() {
printing::PageRange range;
// Render 10 pages at a time.
range.from = last_page_printed_ + 1;
range.to = last_page_printed_ + kPageCountPerBatch;
std::vector<printing::PageRange> page_ranges;
page_ranges.push_back(range);
int printer_dpi = ::GetDeviceCaps(printer_dc_.Get(), LOGPIXELSX);
int dc_width = GetDeviceCaps(printer_dc_.Get(), PHYSICALWIDTH);
int dc_height = GetDeviceCaps(printer_dc_.Get(), PHYSICALHEIGHT);
gfx::Rect render_area(0, 0, dc_width, dc_height);
g_service_process->io_thread()->message_loop_proxy()->PostTask(
FROM_HERE,
NewRunnableMethod(
this,
&JobSpoolerWin::Core::RenderPDFPagesInSandbox,
print_data_file_path_,
render_area,
printer_dpi,
page_ranges,
base::MessageLoopProxy::current()));
}
// Called on the service process IO thread.
void RenderPDFPagesInSandbox(
const FilePath& pdf_path, const gfx::Rect& render_area,
int render_dpi, const std::vector<printing::PageRange>& page_ranges,
const scoped_refptr<base::MessageLoopProxy>&
client_message_loop_proxy) {
DCHECK(g_service_process->io_thread()->message_loop_proxy()->
BelongsToCurrentThread());
scoped_ptr<ServiceUtilityProcessHost> utility_host(
new ServiceUtilityProcessHost(this, client_message_loop_proxy));
// TODO(gene): For now we disabling autorotation for CloudPrinting.
// Landscape/Portrait setting is passed in the print ticket and
// server is generating portrait PDF always.
// We should enable autorotation once server will be able to generate
// PDF that matches paper size and orientation.
if (utility_host->StartRenderPDFPagesToMetafile(
pdf_path,
printing::PdfRenderSettings(render_area, render_dpi, false),
page_ranges)) {
// The object will self-destruct when the child process dies.
utility_host.release();
}
}
bool PrintXPSDocument(const std::string& printer_name,
const std::string& job_title,
const FilePath& print_data_file_path,
const std::string& print_ticket) {
if (!printing::XPSPrintModule::Init())
return false;
job_progress_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
if (!job_progress_event_.Get())
return false;
should_couninit_ = SUCCEEDED(CoInitializeEx(NULL,
COINIT_MULTITHREADED));
base::win::ScopedComPtr<IXpsPrintJobStream> doc_stream;
base::win::ScopedComPtr<IXpsPrintJobStream> print_ticket_stream;
bool ret = false;
// Use nested SUCCEEDED checks because we want a common return point.
if (SUCCEEDED(printing::XPSPrintModule::StartXpsPrintJob(
UTF8ToWide(printer_name).c_str(),
UTF8ToWide(job_title).c_str(),
NULL,
job_progress_event_.Get(),
NULL,
NULL,
NULL,
xps_print_job_.Receive(),
doc_stream.Receive(),
print_ticket_stream.Receive()))) {
ULONG bytes_written = 0;
if (SUCCEEDED(print_ticket_stream->Write(print_ticket.c_str(),
print_ticket.length(),
&bytes_written))) {
DCHECK(bytes_written == print_ticket.length());
if (SUCCEEDED(print_ticket_stream->Close())) {
std::string document_data;
file_util::ReadFileToString(print_data_file_path, &document_data);
bytes_written = 0;
if (SUCCEEDED(doc_stream->Write(document_data.c_str(),
document_data.length(),
&bytes_written))) {
DCHECK(bytes_written == document_data.length());
if (SUCCEEDED(doc_stream->Close())) {
job_progress_watcher_.StartWatching(job_progress_event_.Get(),
this);
ret = true;
}
}
}
}
}
if (!ret) {
if (xps_print_job_) {
xps_print_job_->Cancel();
xps_print_job_.Release();
}
if (should_couninit_) {
CoUninitialize();
should_couninit_ = false;
}
}
return ret;
}
// Some Cairo-generated PDFs from Chrome OS result in huge metafiles.
// So the PageCountPerBatch is set to 1 for now.
// TODO(sanjeevr): Figure out a smarter way to determine the pages per
// batch. Filed a bug to track this at
// http://code.google.com/p/chromium/issues/detail?id=57350.
static const int kPageCountPerBatch = 1;
int last_page_printed_;
PlatformJobId job_id_;
PrintSystem::JobSpooler::Delegate* delegate_;
int saved_dc_;
base::win::ScopedCreateDC printer_dc_;
FilePath print_data_file_path_;
base::win::ScopedHandle job_progress_event_;
base::win::ObjectWatcher job_progress_watcher_;
base::win::ScopedComPtr<IXpsPrintJob> xps_print_job_;
bool should_couninit_;
DISALLOW_COPY_AND_ASSIGN(JobSpoolerWin::Core);
};
scoped_refptr<Core> core_;
DISALLOW_COPY_AND_ASSIGN(JobSpoolerWin);
};
// A helper class to handle the response from the utility process to the
// request to fetch printer capabilities and defaults.
class PrinterCapsHandler : public ServiceUtilityProcessHost::Client {
public:
PrinterCapsHandler(
const std::string& printer_name,
PrinterCapsAndDefaultsCallback* callback)
: printer_name_(printer_name), callback_(callback) {
}
virtual void Start() {
g_service_process->io_thread()->message_loop_proxy()->PostTask(
FROM_HERE,
NewRunnableMethod(
this,
&PrinterCapsHandler::GetPrinterCapsAndDefaultsImpl,
base::MessageLoopProxy::current()));
}
virtual void OnChildDied() {
OnGetPrinterCapsAndDefaultsFailed(printer_name_);
}
virtual void OnGetPrinterCapsAndDefaultsSucceeded(
const std::string& printer_name,
const printing::PrinterCapsAndDefaults& caps_and_defaults) {
callback_->Run(true, printer_name, caps_and_defaults);
callback_.reset();
Release();
}
virtual void OnGetPrinterCapsAndDefaultsFailed(
const std::string& printer_name) {
printing::PrinterCapsAndDefaults caps_and_defaults;
callback_->Run(false, printer_name, caps_and_defaults);
callback_.reset();
Release();
}
private:
// Called on the service process IO thread.
void GetPrinterCapsAndDefaultsImpl(
const scoped_refptr<base::MessageLoopProxy>&
client_message_loop_proxy) {
DCHECK(g_service_process->io_thread()->message_loop_proxy()->
BelongsToCurrentThread());
scoped_ptr<ServiceUtilityProcessHost> utility_host(
new ServiceUtilityProcessHost(this, client_message_loop_proxy));
if (utility_host->StartGetPrinterCapsAndDefaults(printer_name_)) {
// The object will self-destruct when the child process dies.
utility_host.release();
} else {
client_message_loop_proxy->PostTask(
FROM_HERE,
NewRunnableMethod(
this,
&PrinterCapsHandler::OnGetPrinterCapsAndDefaultsFailed,
printer_name_));
}
}
std::string printer_name_;
scoped_ptr<PrinterCapsAndDefaultsCallback> callback_;
};
virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher();
virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher(
const std::string& printer_name);
virtual PrintSystem::JobSpooler* CreateJobSpooler();
virtual std::string GetSupportedMimeTypes();
private:
scoped_refptr<printing::PrintBackend> print_backend_;
};
PrintSystemWin::PrintSystemWin() {
print_backend_ = printing::PrintBackend::CreateInstance(NULL);
}
PrintSystem::PrintSystemResult PrintSystemWin::Init() {
if (!printing::XPSModule::Init()) {
std::string message = l10n_util::GetStringUTF8(
IDS_CLOUD_PRINT_XPS_UNAVAILABLE);
return PrintSystemResult(false, message);
}
return PrintSystemResult(true, std::string());
}
PrintSystem::PrintSystemResult PrintSystemWin::EnumeratePrinters(
printing::PrinterList* printer_list) {
bool ret = print_backend_->EnumeratePrinters(printer_list);
return PrintSystemResult(ret, std::string());
}
void PrintSystemWin::GetPrinterCapsAndDefaults(
const std::string& printer_name,
PrinterCapsAndDefaultsCallback* callback) {
// Launch as child process to retrieve the capabilities and defaults because
// this involves invoking a printer driver DLL and crashes have been known to
// occur.
PrinterCapsHandler* handler =
new PrinterCapsHandler(printer_name, callback);
handler->AddRef();
handler->Start();
}
bool PrintSystemWin::IsValidPrinter(const std::string& printer_name) {
return print_backend_->IsValidPrinter(printer_name);
}
bool PrintSystemWin::ValidatePrintTicket(
const std::string& printer_name,
const std::string& print_ticket_data) {
printing::ScopedXPSInitializer xps_initializer;
if (!xps_initializer.initialized()) {
// TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
return false;
}
bool ret = false;
HPTPROVIDER provider = NULL;
printing::XPSModule::OpenProvider(UTF8ToWide(printer_name.c_str()),
1,
&provider);
if (provider) {
base::win::ScopedComPtr<IStream> print_ticket_stream;
CreateStreamOnHGlobal(NULL, TRUE, print_ticket_stream.Receive());
ULONG bytes_written = 0;
print_ticket_stream->Write(print_ticket_data.c_str(),
print_ticket_data.length(),
&bytes_written);
DCHECK(bytes_written == print_ticket_data.length());
LARGE_INTEGER pos = {0};
ULARGE_INTEGER new_pos = {0};
print_ticket_stream->Seek(pos, STREAM_SEEK_SET, &new_pos);
base::win::ScopedBstr error;
base::win::ScopedComPtr<IStream> result_ticket_stream;
CreateStreamOnHGlobal(NULL, TRUE, result_ticket_stream.Receive());
ret = SUCCEEDED(printing::XPSModule::MergeAndValidatePrintTicket(
provider,
print_ticket_stream.get(),
NULL,
kPTJobScope,
result_ticket_stream.get(),
error.Receive()));
printing::XPSModule::CloseProvider(provider);
}
return ret;
}
bool PrintSystemWin::GetJobDetails(const std::string& printer_name,
PlatformJobId job_id,
PrintJobDetails *job_details) {
DCHECK(job_details);
HANDLE printer_handle = NULL;
std::wstring printer_name_wide = UTF8ToWide(printer_name);
OpenPrinter(const_cast<LPTSTR>(printer_name_wide.c_str()), &printer_handle,
NULL);
DCHECK(printer_handle);
bool ret = false;
if (printer_handle) {
DWORD bytes_needed = 0;
GetJob(printer_handle, job_id, 1, NULL, 0, &bytes_needed);
DWORD last_error = GetLastError();
if (ERROR_INVALID_PARAMETER != last_error) {
// ERROR_INVALID_PARAMETER normally means that the job id is not valid.
DCHECK(last_error == ERROR_INSUFFICIENT_BUFFER);
scoped_array<BYTE> job_info_buffer(new BYTE[bytes_needed]);
if (GetJob(printer_handle, job_id, 1, job_info_buffer.get(), bytes_needed,
&bytes_needed)) {
JOB_INFO_1 *job_info =
reinterpret_cast<JOB_INFO_1 *>(job_info_buffer.get());
if (job_info->pStatus) {
WideToUTF8(job_info->pStatus, wcslen(job_info->pStatus),
&job_details->status_message);
}
job_details->platform_status_flags = job_info->Status;
if ((job_info->Status & JOB_STATUS_COMPLETE) ||
(job_info->Status & JOB_STATUS_PRINTED)) {
job_details->status = PRINT_JOB_STATUS_COMPLETED;
} else if (job_info->Status & JOB_STATUS_ERROR) {
job_details->status = PRINT_JOB_STATUS_ERROR;
} else {
job_details->status = PRINT_JOB_STATUS_IN_PROGRESS;
}
job_details->total_pages = job_info->TotalPages;
job_details->pages_printed = job_info->PagesPrinted;
ret = true;
}
}
ClosePrinter(printer_handle);
}
return ret;
}
PrintSystem::PrintServerWatcher*
PrintSystemWin::CreatePrintServerWatcher() {
return new PrintServerWatcherWin();
}
PrintSystem::PrinterWatcher* PrintSystemWin::CreatePrinterWatcher(
const std::string& printer_name) {
DCHECK(!printer_name.empty());
return new PrinterWatcherWin(printer_name);
}
PrintSystem::JobSpooler*
PrintSystemWin::CreateJobSpooler() {
return new JobSpoolerWin();
}
std::string PrintSystemWin::GetSupportedMimeTypes() {
if (printing::XPSPrintModule::Init())
return "application/vnd.ms-xpsdocument,application/pdf";
return "application/pdf";
}
std::string PrintSystem::GenerateProxyId() {
GUID proxy_id = {0};
HRESULT hr = UuidCreate(&proxy_id);
DCHECK(SUCCEEDED(hr));
wchar_t* proxy_id_as_string = NULL;
UuidToString(&proxy_id, reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string));
DCHECK(proxy_id_as_string);
std::string ret;
WideToUTF8(proxy_id_as_string, wcslen(proxy_id_as_string), &ret);
RpcStringFree(reinterpret_cast<RPC_WSTR *>(&proxy_id_as_string));
return ret;
}
scoped_refptr<PrintSystem> PrintSystem::CreateInstance(
const base::DictionaryValue* print_system_settings) {
return new PrintSystemWin;
}
} // namespace cloud_print