#include "printing/printed_document.h"
#include "printing/printed_page.h"
+#if defined(OS_WIN)
+#include "chrome/browser/printing/pdf_to_emf_converter.h"
+#include "printing/pdf_render_settings.h"
+#endif
+
using base::TimeDelta;
namespace {
namespace printing {
PrintJob::PrintJob()
- : ui_message_loop_(base::MessageLoop::current()),
- source_(NULL),
+ : source_(NULL),
worker_(),
settings_(),
is_job_pending_(false),
is_canceling_(false),
quit_factory_(this) {
- DCHECK(ui_message_loop_);
// This is normally a UI message loop, but in unit tests, the message loop is
// of the 'default' type.
DCHECK(base::MessageLoopForUI::IsCurrent() ||
- ui_message_loop_->type() == base::MessageLoop::TYPE_DEFAULT);
- ui_message_loop_->AddDestructionObserver(this);
+ base::MessageLoop::current()->type() ==
+ base::MessageLoop::TYPE_DEFAULT);
}
PrintJob::~PrintJob() {
- ui_message_loop_->RemoveDestructionObserver(this);
// The job should be finished (or at least canceled) when it is destroyed.
DCHECK(!is_job_pending_);
DCHECK(!is_canceling_);
- if (worker_.get())
- DCHECK(worker_->message_loop() == NULL);
- DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
+ DCHECK(!worker_ || !worker_->IsRunning());
+ DCHECK(RunsTasksOnCurrentThread());
}
void PrintJob::Initialize(PrintJobWorkerOwner* job,
void PrintJob::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
- DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
+ DCHECK(RunsTasksOnCurrentThread());
switch (type) {
case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
return NULL;
}
-base::MessageLoop* PrintJob::message_loop() {
- return ui_message_loop_;
-}
-
const PrintSettings& PrintJob::settings() const {
return settings_;
}
return document_->cookie();
}
-void PrintJob::WillDestroyCurrentMessageLoop() {
- NOTREACHED();
-}
-
void PrintJob::StartPrinting() {
- DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
- DCHECK(worker_->message_loop());
+ DCHECK(RunsTasksOnCurrentThread());
+ DCHECK(worker_->IsRunning());
DCHECK(!is_job_pending_);
- if (!worker_->message_loop() || is_job_pending_)
+ if (!worker_->IsRunning() || is_job_pending_)
return;
// Real work is done in PrintJobWorker::StartPrinting().
- worker_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&HoldRefCallback, make_scoped_refptr(this),
- base::Bind(&PrintJobWorker::StartPrinting,
- base::Unretained(worker_.get()), document_)));
+ worker_->PostTask(FROM_HERE,
+ base::Bind(&HoldRefCallback,
+ make_scoped_refptr(this),
+ base::Bind(&PrintJobWorker::StartPrinting,
+ base::Unretained(worker_.get()),
+ document_)));
// Set the flag right now.
is_job_pending_ = true;
}
void PrintJob::Stop() {
- DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
+ DCHECK(RunsTasksOnCurrentThread());
if (quit_factory_.HasWeakPtrs()) {
// In case we're running a nested message loop to wait for a job to finish,
// Be sure to live long enough.
scoped_refptr<PrintJob> handle(this);
- if (worker_->message_loop()) {
+ if (worker_->IsRunning()) {
ControlledWorkerShutdown();
} else {
// Flush the cached document.
// Be sure to live long enough.
scoped_refptr<PrintJob> handle(this);
- DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
- base::MessageLoop* worker_loop =
- worker_.get() ? worker_->message_loop() : NULL;
- if (worker_loop) {
+ DCHECK(RunsTasksOnCurrentThread());
+ if (worker_ && worker_->IsRunning()) {
// Call this right now so it renders the context invalid. Do not use
// InvokeLater since it would take too much time.
worker_->Cancel();
return document_.get();
}
+#if defined(OS_WIN)
+
+class PrintJob::PdfToEmfState {
+ public:
+ PdfToEmfState(const gfx::Size& page_size, const gfx::Rect& content_area)
+ : page_count_(0),
+ current_page_(0),
+ pages_in_progress_(0),
+ page_size_(page_size),
+ content_area_(content_area),
+ converter_(PdfToEmfConverter::CreateDefault()) {}
+
+ void Start(const scoped_refptr<base::RefCountedMemory>& data,
+ const PdfRenderSettings& conversion_settings,
+ const PdfToEmfConverter::StartCallback& start_callback) {
+ converter_->Start(data, conversion_settings, start_callback);
+ }
+
+ void GetMorePages(
+ const PdfToEmfConverter::GetPageCallback& get_page_callback) {
+ const int kMaxNumberOfTempFilesPerDocument = 3;
+ while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument &&
+ current_page_ < page_count_) {
+ ++pages_in_progress_;
+ converter_->GetPage(current_page_++, get_page_callback);
+ }
+ }
+
+ void OnPageProcessed(
+ const PdfToEmfConverter::GetPageCallback& get_page_callback) {
+ --pages_in_progress_;
+ GetMorePages(get_page_callback);
+ // Release converter if we don't need this any more.
+ if (!pages_in_progress_ && current_page_ >= page_count_)
+ converter_.reset();
+ }
+
+ void set_page_count(int page_count) { page_count_ = page_count; }
+ gfx::Size page_size() const { return page_size_; }
+ gfx::Rect content_area() const { return content_area_; }
+
+ private:
+ int page_count_;
+ int current_page_;
+ int pages_in_progress_;
+ gfx::Size page_size_;
+ gfx::Rect content_area_;
+ scoped_ptr<PdfToEmfConverter> converter_;
+};
+
+void PrintJob::StartPdfToEmfConversion(
+ const scoped_refptr<base::RefCountedMemory>& bytes,
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area) {
+ DCHECK(!ptd_to_emf_state_.get());
+ ptd_to_emf_state_.reset(new PdfToEmfState(page_size, content_area));
+ const int kPrinterDpi = settings().dpi();
+ ptd_to_emf_state_->Start(
+ bytes,
+ printing::PdfRenderSettings(content_area, kPrinterDpi, true),
+ base::Bind(&PrintJob::OnPdfToEmfStarted, this));
+}
+
+void PrintJob::OnPdfToEmfStarted(int page_count) {
+ if (page_count <= 0) {
+ ptd_to_emf_state_.reset();
+ Cancel();
+ return;
+ }
+ ptd_to_emf_state_->set_page_count(page_count);
+ ptd_to_emf_state_->GetMorePages(
+ base::Bind(&PrintJob::OnPdfToEmfPageConverted, this));
+}
+
+void PrintJob::OnPdfToEmfPageConverted(int page_number,
+ double scale_factor,
+ scoped_ptr<MetafilePlayer> emf) {
+ DCHECK(ptd_to_emf_state_);
+ if (!document_.get() || !emf) {
+ ptd_to_emf_state_.reset();
+ Cancel();
+ return;
+ }
+
+ // Update the rendered document. It will send notifications to the listener.
+ document_->SetPage(page_number,
+ emf.Pass(),
+ scale_factor,
+ ptd_to_emf_state_->page_size(),
+ ptd_to_emf_state_->content_area());
+
+ ptd_to_emf_state_->GetMorePages(
+ base::Bind(&PrintJob::OnPdfToEmfPageConverted, this));
+}
+
+#endif // OS_WIN
+
void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
if (document_.get() == new_document)
return;
settings_ = document_->settings();
}
- if (worker_.get() && worker_->message_loop()) {
+ if (worker_) {
DCHECK(!is_job_pending_);
// Sync the document with the worker.
- worker_->message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&HoldRefCallback, make_scoped_refptr(this),
- base::Bind(&PrintJobWorker::OnDocumentChanged,
- base::Unretained(worker_.get()), document_)));
+ worker_->PostTask(FROM_HERE,
+ base::Bind(&HoldRefCallback,
+ make_scoped_refptr(this),
+ base::Bind(&PrintJobWorker::OnDocumentChanged,
+ base::Unretained(worker_.get()),
+ document_)));
}
}
}
case JobEventDetails::NEW_DOC:
case JobEventDetails::NEW_PAGE:
- case JobEventDetails::PAGE_DONE:
case JobEventDetails::JOB_DONE:
case JobEventDetails::ALL_PAGES_REQUESTED: {
// Don't care.
FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
break;
}
+ case JobEventDetails::PAGE_DONE:
+#if defined(OS_WIN)
+ ptd_to_emf_state_->OnPageProcessed(
+ base::Bind(&PrintJob::OnPdfToEmfPageConverted, this));
+#endif // OS_WIN
+ break;
default: {
NOTREACHED();
break;
}
void PrintJob::ControlledWorkerShutdown() {
- DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
+ DCHECK(RunsTasksOnCurrentThread());
// The deadlock this code works around is specific to window messaging on
// Windows, so we aren't likely to need it on any other platforms.