Browse Source

Make Print API work on Windows.

Haojian Wu 10 years ago
parent
commit
ff87592722

+ 8 - 0
atom/app/atom_main_delegate.cc

@@ -14,6 +14,7 @@
 #include "base/debug/stack_trace.h"
 #include "base/environment.h"
 #include "base/logging.h"
+#include "chrome/utility/chrome_content_utility_client.h"
 #include "content/public/common/content_switches.h"
 #include "ui/base/resource/resource_bundle.h"
 
@@ -94,6 +95,13 @@ content::ContentRendererClient*
   return renderer_client_.get();
 }
 
+content::ContentUtilityClient* AtomMainDelegate::CreateContentUtilityClient() {
+#if defined(OS_WIN)
+  utility_client_.reset(new AtomContentUtilityClient);
+  return utility_client_.get();
+#endif
+}
+
 scoped_ptr<brightray::ContentClient> AtomMainDelegate::CreateContentClient() {
   return scoped_ptr<brightray::ContentClient>(new AtomContentClient).Pass();
 }

+ 2 - 0
atom/app/atom_main_delegate.h

@@ -21,6 +21,7 @@ class AtomMainDelegate : public brightray::MainDelegate {
   void PreSandboxStartup() override;
   content::ContentBrowserClient* CreateContentBrowserClient() override;
   content::ContentRendererClient* CreateContentRendererClient() override;
+  content::ContentUtilityClient* CreateContentUtilityClient() override;
 
   // brightray::MainDelegate:
   scoped_ptr<brightray::ContentClient> CreateContentClient() override;
@@ -35,6 +36,7 @@ class AtomMainDelegate : public brightray::MainDelegate {
   brightray::ContentClient content_client_;
   scoped_ptr<content::ContentBrowserClient> browser_client_;
   scoped_ptr<content::ContentRendererClient> renderer_client_;
+  scoped_ptr<content::ContentUtilityClient> utility_client_;
 
   DISALLOW_COPY_AND_ASSIGN(AtomMainDelegate);
 };

+ 76 - 0
atom/utility/atom_content_utility_client.cc

@@ -0,0 +1,76 @@
+// Copyright (c) 2015 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/utility/atom_content_utility_client.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "chrome/common/chrome_utility_messages.h"
+#include "chrome/utility/utility_message_handler.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/utility/utility_thread.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_message_macros.h"
+
+
+#if defined(OS_WIN)
+#include "chrome/utility/printing_handler.h"
+#endif
+
+
+namespace {
+
+bool Send(IPC::Message* message) {
+  return content::UtilityThread::Get()->Send(message);
+}
+
+void ReleaseProcessIfNeeded() {
+  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+}
+
+}  // namespace
+
+namespace atom {
+
+int64_t AtomContentUtilityClient::max_ipc_message_size_ =
+    IPC::Channel::kMaximumMessageSize;
+
+AtomContentUtilityClient::AtomContentUtilityClient()
+    : filter_messages_(false) {
+  handlers_.push_back(new PrintingHandler());
+}
+
+AtomContentUtilityClient::~AtomContentUtilityClient() {
+}
+
+void AtomContentUtilityClient::UtilityThreadStarted() {
+}
+
+bool AtomContentUtilityClient::OnMessageReceived(
+    const IPC::Message& message) {
+  if (filter_messages_ && !ContainsKey(message_id_whitelist_, message.type()))
+    return false;
+
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(AtomContentUtilityClient, message)
+    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_StartupPing, OnStartupPing)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  for (Handlers::iterator it = handlers_.begin();
+       !handled && it != handlers_.end(); ++it) {
+    handled = (*it)->OnMessageReceived(message);
+  }
+
+  return handled;
+}
+
+void AtomContentUtilityClient::OnStartupPing() {
+  Send(new ChromeUtilityHostMsg_ProcessStarted);
+  // Don't release the process, we assume further messages are on the way.
+}
+
+}  // namespace atom

+ 57 - 0
atom/utility/atom_content_utility_client.h

@@ -0,0 +1,57 @@
+// Copyright (c) 2015 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_UTILITY_ATOM_CONTENT_UTILITY_CLIENT_H_
+#define ATOM_UTILITY_ATOM_CONTENT_UTILITY_CLIENT_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_vector.h"
+#include "content/public/utility/content_utility_client.h"
+#include "ipc/ipc_platform_file.h"
+
+namespace base {
+class FilePath;
+struct FileDescriptor;
+}
+
+class UtilityMessageHandler;
+
+namespace atom {
+
+class AtomContentUtilityClient : public content::ContentUtilityClient {
+ public:
+  AtomContentUtilityClient();
+  ~AtomContentUtilityClient() override;
+
+  void UtilityThreadStarted() override;
+  bool OnMessageReceived(const IPC::Message& message) override;
+
+
+  static void set_max_ipc_message_size_for_test(int64_t max_message_size) {
+    max_ipc_message_size_ = max_message_size;
+  }
+
+ private:
+  void OnStartupPing();
+
+  typedef ScopedVector<UtilityMessageHandler> Handlers;
+  Handlers handlers_;
+
+  // Flag to enable whitelisting.
+  bool filter_messages_;
+  // A list of message_ids to filter.
+  std::set<int> message_id_whitelist_;
+  // Maximum IPC msg size (default to kMaximumMessageSize; override for testing)
+  static int64_t max_ipc_message_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(AtomContentUtilityClient);
+};
+
+}  // namespace atom
+
+#endif  // ATOM_UTILITY_ATOM_CONTENT_UTILITY_CLIENT_H_ 

+ 495 - 0
chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc

@@ -0,0 +1,495 @@
+// Copyright 2014 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/browser/printing/pdf_to_emf_converter.h"
+
+#include <queue>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/logging.h"
+#include "chrome/common/chrome_utility_messages.h"
+#include "chrome/common/print_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/utility_process_host.h"
+#include "content/public/browser/utility_process_host_client.h"
+#include "printing/emf_win.h"
+#include "printing/pdf_render_settings.h"
+
+namespace printing {
+
+namespace {
+
+using content::BrowserThread;
+
+class PdfToEmfConverterImpl;
+
+// Allows to delete temporary directory after all temporary files created inside
+// are closed. Windows cannot delete directory with opened files. Directory is
+// used to store PDF and metafiles. PDF should be gone by the time utility
+// process exits. Metafiles should be gone when all LazyEmf destroyed.
+class RefCountedTempDir
+    : public base::RefCountedThreadSafe<RefCountedTempDir,
+                                        BrowserThread::DeleteOnFileThread> {
+ public:
+  RefCountedTempDir() { ignore_result(temp_dir_.CreateUniqueTempDir()); }
+  bool IsValid() const { return temp_dir_.IsValid(); }
+  const base::FilePath& GetPath() const { return temp_dir_.path(); }
+
+ private:
+  friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>;
+  friend class base::DeleteHelper<RefCountedTempDir>;
+  ~RefCountedTempDir() {}
+
+  base::ScopedTempDir temp_dir_;
+  DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir);
+};
+
+typedef scoped_ptr<base::File, BrowserThread::DeleteOnFileThread>
+    ScopedTempFile;
+
+// Wrapper for Emf to keep only file handle in memory, and load actual data only
+// on playback. Emf::InitFromFile() can play metafile directly from disk, but it
+// can't open file handles. We need file handles to reliably delete temporary
+// files, and to efficiently interact with utility process.
+class LazyEmf : public MetafilePlayer {
+ public:
+  LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file)
+      : temp_dir_(temp_dir), file_(file.Pass()) {}
+  virtual ~LazyEmf() { Close(); }
+
+  virtual bool SafePlayback(HDC hdc) const override;
+  virtual bool SaveTo(base::File* file) const override;
+
+ private:
+  void Close() const;
+  bool LoadEmf(Emf* emf) const;
+
+  mutable scoped_refptr<RefCountedTempDir> temp_dir_;
+  mutable ScopedTempFile file_;  // Mutable because of consts in base class.
+
+  DISALLOW_COPY_AND_ASSIGN(LazyEmf);
+};
+
+// Converts PDF into EMF.
+// Class uses 3 threads: UI, IO and FILE.
+// Internal workflow is following:
+// 1. Create instance on the UI thread. (files_, settings_,)
+// 2. Create pdf file on the FILE thread.
+// 3. Start utility process and start conversion on the IO thread.
+// 4. Utility process returns page count.
+// 5. For each page:
+//   1. Clients requests page with file handle to a temp file.
+//   2. Utility converts the page, save it to the file and reply.
+//
+// All these steps work sequentially, so no data should be accessed
+// simultaneously by several threads.
+class PdfToEmfUtilityProcessHostClient
+    : public content::UtilityProcessHostClient {
+ public:
+  PdfToEmfUtilityProcessHostClient(
+      base::WeakPtr<PdfToEmfConverterImpl> converter,
+      const PdfRenderSettings& settings);
+
+  void Start(const scoped_refptr<base::RefCountedMemory>& data,
+             const PdfToEmfConverter::StartCallback& start_callback);
+
+  void GetPage(int page_number,
+               const PdfToEmfConverter::GetPageCallback& get_page_callback);
+
+  void Stop();
+
+  // UtilityProcessHostClient implementation.
+  virtual void OnProcessCrashed(int exit_code) override;
+  virtual void OnProcessLaunchFailed() override;
+  virtual bool OnMessageReceived(const IPC::Message& message) override;
+
+ private:
+  class GetPageCallbackData {
+    MOVE_ONLY_TYPE_FOR_CPP_03(GetPageCallbackData, RValue);
+
+   public:
+    GetPageCallbackData(int page_number,
+                        PdfToEmfConverter::GetPageCallback callback)
+        : page_number_(page_number), callback_(callback) {}
+
+    // Move constructor for STL.
+    GetPageCallbackData(RValue other) { this->operator=(other); }
+
+    // Move assignment for STL.
+    GetPageCallbackData& operator=(RValue rhs) {
+      page_number_ = rhs.object->page_number_;
+      callback_ = rhs.object->callback_;
+      emf_ = rhs.object->emf_.Pass();
+      return *this;
+    }
+
+    int page_number() const { return page_number_; }
+    const PdfToEmfConverter::GetPageCallback& callback() const {
+      return callback_;
+    }
+    ScopedTempFile emf() { return emf_.Pass(); }
+    void set_emf(ScopedTempFile emf) { emf_ = emf.Pass(); }
+
+   private:
+    int page_number_;
+    PdfToEmfConverter::GetPageCallback callback_;
+    ScopedTempFile emf_;
+  };
+
+  virtual ~PdfToEmfUtilityProcessHostClient();
+
+  bool Send(IPC::Message* msg);
+
+  // Message handlers.
+  void OnProcessStarted();
+  void OnPageCount(int page_count);
+  void OnPageDone(bool success, float scale_factor);
+
+  void OnFailed();
+  void OnTempPdfReady(ScopedTempFile pdf);
+  void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf);
+
+  scoped_refptr<RefCountedTempDir> temp_dir_;
+
+  // Used to suppress callbacks after PdfToEmfConverterImpl is deleted.
+  base::WeakPtr<PdfToEmfConverterImpl> converter_;
+  PdfRenderSettings settings_;
+  scoped_refptr<base::RefCountedMemory> data_;
+
+  // Document loaded callback.
+  PdfToEmfConverter::StartCallback start_callback_;
+
+  // Process host for IPC.
+  base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
+
+  // Queue of callbacks for GetPage() requests. Utility process should reply
+  // with PageDone in the same order as requests were received.
+  // Use containers that keeps element pointers valid after push() and pop().
+  typedef std::queue<GetPageCallbackData> GetPageCallbacks;
+  GetPageCallbacks get_page_callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
+};
+
+class PdfToEmfConverterImpl : public PdfToEmfConverter {
+ public:
+  PdfToEmfConverterImpl();
+
+  virtual ~PdfToEmfConverterImpl();
+
+  virtual void Start(const scoped_refptr<base::RefCountedMemory>& data,
+                     const PdfRenderSettings& conversion_settings,
+                     const StartCallback& start_callback) override;
+
+  virtual void GetPage(int page_number,
+                       const GetPageCallback& get_page_callback) override;
+
+  // Helps to cancel callbacks if this object is destroyed.
+  void RunCallback(const base::Closure& callback);
+
+ private:
+  scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_;
+  base::WeakPtrFactory<PdfToEmfConverterImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl);
+};
+
+ScopedTempFile CreateTempFile(scoped_refptr<RefCountedTempDir>* temp_dir) {
+  if (!temp_dir->get())
+    *temp_dir = new RefCountedTempDir();
+  ScopedTempFile file;
+  if (!(*temp_dir)->IsValid())
+    return file.Pass();
+  base::FilePath path;
+  if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path))
+    return file.Pass();
+  file.reset(new base::File(path,
+                            base::File::FLAG_CREATE_ALWAYS |
+                            base::File::FLAG_WRITE |
+                            base::File::FLAG_READ |
+                            base::File::FLAG_DELETE_ON_CLOSE |
+                            base::File::FLAG_TEMPORARY));
+  if (!file->IsValid())
+    file.reset();
+  return file.Pass();
+}
+
+ScopedTempFile CreateTempPdfFile(
+    const scoped_refptr<base::RefCountedMemory>& data,
+    scoped_refptr<RefCountedTempDir>* temp_dir) {
+  DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+
+  ScopedTempFile pdf_file = CreateTempFile(temp_dir);
+  if (!pdf_file ||
+      static_cast<int>(data->size()) !=
+          pdf_file->WriteAtCurrentPos(data->front_as<char>(), data->size())) {
+    pdf_file.reset();
+  }
+  pdf_file->Seek(base::File::FROM_BEGIN, 0);
+  return pdf_file.Pass();
+}
+
+bool LazyEmf::SafePlayback(HDC hdc) const {
+  Emf emf;
+  bool result = LoadEmf(&emf) && emf.SafePlayback(hdc);
+  // TODO(vitalybuka): Fix destruction of metafiles. For some reasons
+  // instances of Emf are not deleted. crbug.com/411683
+  // It's known that the Emf going to be played just once to a printer. So just
+  // release file here.
+  Close();
+  return result;
+}
+
+bool LazyEmf::SaveTo(base::File* file) const {
+  Emf emf;
+  return LoadEmf(&emf) && emf.SaveTo(file);
+}
+
+void LazyEmf::Close() const {
+  file_.reset();
+  temp_dir_ = NULL;
+}
+
+bool LazyEmf::LoadEmf(Emf* emf) const {
+  file_->Seek(base::File::FROM_BEGIN, 0);
+  int64 size = file_->GetLength();
+  if (size <= 0)
+    return false;
+  std::vector<char> data(size);
+  if (file_->ReadAtCurrentPos(data.data(), data.size()) != size)
+    return false;
+  return emf->InitFromData(data.data(), data.size());
+}
+
+PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
+    base::WeakPtr<PdfToEmfConverterImpl> converter,
+    const PdfRenderSettings& settings)
+    : converter_(converter), settings_(settings) {
+}
+
+PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
+}
+
+void PdfToEmfUtilityProcessHostClient::Start(
+    const scoped_refptr<base::RefCountedMemory>& data,
+    const PdfToEmfConverter::StartCallback& start_callback) {
+  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    BrowserThread::PostTask(BrowserThread::IO,
+                            FROM_HERE,
+                            base::Bind(&PdfToEmfUtilityProcessHostClient::Start,
+                                       this,
+                                       data,
+                                       start_callback));
+    return;
+  }
+  data_ = data;
+
+  // Store callback before any OnFailed() call to make it called on failure.
+  start_callback_ = start_callback;
+
+  // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
+  // gdiplus.dll, change how rendering happens, and not be able to correctly
+  // generate when sent to a metafile DC.
+  utility_process_host_ =
+      content::UtilityProcessHost::Create(
+          this, base::MessageLoop::current()->message_loop_proxy())
+          ->AsWeakPtr();
+  if (!utility_process_host_)
+    return OnFailed();
+  // Should reply with OnProcessStarted().
+  Send(new ChromeUtilityMsg_StartupPing);
+}
+
+void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!utility_process_host_)
+    return OnFailed();
+
+  scoped_refptr<base::RefCountedMemory> data = data_;
+  data_ = NULL;
+  BrowserThread::PostTaskAndReplyWithResult(
+      BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&CreateTempPdfFile, data, &temp_dir_),
+      base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this));
+}
+
+void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!utility_process_host_)
+    return OnFailed();
+  base::ProcessHandle process = utility_process_host_->GetData().handle;
+  // Should reply with OnPageCount().
+  Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
+      IPC::GetFileHandleForProcess(pdf->GetPlatformFile(), process, false),
+      settings_));
+}
+
+void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (start_callback_.is_null())
+    return OnFailed();
+  BrowserThread::PostTask(BrowserThread::UI,
+                          FROM_HERE,
+                          base::Bind(&PdfToEmfConverterImpl::RunCallback,
+                                     converter_,
+                                     base::Bind(start_callback_, page_count)));
+  start_callback_.Reset();
+}
+
+void PdfToEmfUtilityProcessHostClient::GetPage(
+    int page_number,
+    const PdfToEmfConverter::GetPageCallback& get_page_callback) {
+  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    BrowserThread::PostTask(
+        BrowserThread::IO,
+        FROM_HERE,
+        base::Bind(&PdfToEmfUtilityProcessHostClient::GetPage,
+                   this,
+                   page_number,
+                   get_page_callback));
+    return;
+  }
+
+  // Store callback before any OnFailed() call to make it called on failure.
+  get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback));
+
+  if (!utility_process_host_)
+    return OnFailed();
+
+  BrowserThread::PostTaskAndReplyWithResult(
+      BrowserThread::FILE,
+      FROM_HERE,
+      base::Bind(&CreateTempFile, &temp_dir_),
+      base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempEmfReady,
+                 this,
+                 &get_page_callbacks_.back()));
+}
+
+void PdfToEmfUtilityProcessHostClient::OnTempEmfReady(
+    GetPageCallbackData* callback_data,
+    ScopedTempFile emf) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!utility_process_host_)
+    return OnFailed();
+  base::ProcessHandle process = utility_process_host_->GetData().handle;
+  IPC::PlatformFileForTransit transit =
+      IPC::GetFileHandleForProcess(emf->GetPlatformFile(), process, false);
+  callback_data->set_emf(emf.Pass());
+  // Should reply with OnPageDone().
+  Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage(
+      callback_data->page_number(), transit));
+}
+
+void PdfToEmfUtilityProcessHostClient::OnPageDone(bool success,
+                                                  float scale_factor) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (get_page_callbacks_.empty())
+    return OnFailed();
+  scoped_ptr<MetafilePlayer> emf;
+  GetPageCallbackData& data = get_page_callbacks_.front();
+  if (success)
+    emf.reset(new LazyEmf(temp_dir_, data.emf().Pass()));
+  BrowserThread::PostTask(BrowserThread::UI,
+                          FROM_HERE,
+                          base::Bind(&PdfToEmfConverterImpl::RunCallback,
+                                     converter_,
+                                     base::Bind(data.callback(),
+                                                data.page_number(),
+                                                scale_factor,
+                                                base::Passed(&emf))));
+  get_page_callbacks_.pop();
+}
+
+void PdfToEmfUtilityProcessHostClient::Stop() {
+  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
+    BrowserThread::PostTask(
+        BrowserThread::IO,
+        FROM_HERE,
+        base::Bind(&PdfToEmfUtilityProcessHostClient::Stop, this));
+    return;
+  }
+  Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop());
+}
+
+void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
+  OnFailed();
+}
+
+void PdfToEmfUtilityProcessHostClient::OnProcessLaunchFailed() {
+  OnFailed();
+}
+
+bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
+    const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
+    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
+    IPC_MESSAGE_HANDLER(
+        ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount)
+    IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone,
+                        OnPageDone)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+bool PdfToEmfUtilityProcessHostClient::Send(IPC::Message* msg) {
+  if (utility_process_host_)
+    return utility_process_host_->Send(msg);
+  delete msg;
+  return false;
+}
+
+void PdfToEmfUtilityProcessHostClient::OnFailed() {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  if (!start_callback_.is_null())
+    OnPageCount(0);
+  while (!get_page_callbacks_.empty())
+    OnPageDone(false, 0.0f);
+  utility_process_host_.reset();
+}
+
+PdfToEmfConverterImpl::PdfToEmfConverterImpl() : weak_ptr_factory_(this) {
+}
+
+PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
+  if (utility_client_.get())
+    utility_client_->Stop();
+}
+
+void PdfToEmfConverterImpl::Start(
+    const scoped_refptr<base::RefCountedMemory>& data,
+    const PdfRenderSettings& conversion_settings,
+    const StartCallback& start_callback) {
+  DCHECK(!utility_client_.get());
+  utility_client_ = new PdfToEmfUtilityProcessHostClient(
+      weak_ptr_factory_.GetWeakPtr(), conversion_settings);
+  utility_client_->Start(data, start_callback);
+}
+
+void PdfToEmfConverterImpl::GetPage(int page_number,
+                                    const GetPageCallback& get_page_callback) {
+  utility_client_->GetPage(page_number, get_page_callback);
+}
+
+void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  callback.Run();
+}
+
+}  // namespace
+
+PdfToEmfConverter::~PdfToEmfConverter() {
+}
+
+// static
+scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
+  return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
+}
+
+}  // namespace printing

+ 48 - 0
chromium_src/chrome/browser/printing/pdf_to_emf_converter.h

@@ -0,0 +1,48 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_
+#define CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace printing {
+
+class MetafilePlayer;
+class PdfRenderSettings;
+
+class PdfToEmfConverter {
+ public:
+  typedef base::Callback<void(int page_count)> StartCallback;
+  typedef base::Callback<void(int page_number,
+                              float scale_factor,
+                              scoped_ptr<MetafilePlayer> emf)> GetPageCallback;
+
+  virtual ~PdfToEmfConverter();
+
+  static scoped_ptr<PdfToEmfConverter> CreateDefault();
+
+  // Starts conversion of PDF provided as |data|. Calls |start_callback|
+  // with positive |page_count|. |page_count| is 0 if initialization failed.
+  virtual void Start(const scoped_refptr<base::RefCountedMemory>& data,
+                     const PdfRenderSettings& conversion_settings,
+                     const StartCallback& start_callback) = 0;
+
+  // Requests conversion of the page. |page_number| is 0-base page number in
+  // PDF provided in Start() call.
+  // Calls |get_page_callback| after conversion. |emf| of callback in not NULL
+  // if conversion succeeded.
+  virtual void GetPage(int page_number,
+                       const GetPageCallback& get_page_callback) = 0;
+};
+
+}  // namespace printing
+
+#endif  // CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_

+ 103 - 0
chromium_src/chrome/browser/printing/print_job.cc

@@ -17,6 +17,12 @@
 #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 {
@@ -272,6 +278,103 @@ void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
   }
 }
 
+#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,
+                                       float 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::OnDocumentDone() {
   // Be sure to live long enough. The instance could be destroyed by the
   // JOB_DONE broadcast.

+ 18 - 0
chromium_src/chrome/browser/printing/print_job.h

@@ -90,6 +90,19 @@ class PrintJob : public PrintJobWorkerOwner,
   // Access the current printed document. Warning: may be NULL.
   PrintedDocument* document() const;
 
+#if defined(OS_WIN)
+  void StartPdfToEmfConversion(
+      const scoped_refptr<base::RefCountedMemory>& bytes,
+      const gfx::Size& page_size,
+      const gfx::Rect& content_area);
+
+  void OnPdfToEmfStarted(int page_count);
+  void OnPdfToEmfPageConverted(int page_number,
+                               float scale_factor,
+                               scoped_ptr<MetafilePlayer> emf);
+
+#endif  // OS_WIN
+
  protected:
   virtual ~PrintJob();
 
@@ -137,6 +150,11 @@ class PrintJob : public PrintJobWorkerOwner,
   // the notified calls Cancel() again.
   bool is_canceling_;
 
+#if defined(OS_WIN)
+  class PdfToEmfState;
+  scoped_ptr<PdfToEmfState> ptd_to_emf_state_;
+#endif  // OS_WIN
+
   // Used at shutdown so that we can quit a nested message loop.
   base::WeakPtrFactory<PrintJob> quit_factory_;
 

+ 2 - 0
chromium_src/chrome/browser/printing/print_view_manager_base.cc

@@ -156,6 +156,8 @@ void PrintViewManagerBase::OnDidPrintPage(
         params.data_size);
 
     document->DebugDumpData(bytes.get(), FILE_PATH_LITERAL(".pdf"));
+    print_job_->StartPdfToEmfConversion(
+        bytes, params.page_size, params.content_area);
   }
 #endif  // !OS_WIN
 }

+ 217 - 0
chromium_src/chrome/common/chrome_utility_messages.h

@@ -0,0 +1,217 @@
+// Copyright (c) 2012 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.
+
+// Multiply-included message file, so no include guard.
+
+#if defined(OS_WIN)
+#include <Windows.h>
+#endif  // defined(OS_WIN)
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/strings/string16.h"
+#include "base/tuple.h"
+#include "base/values.h"
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_platform_file.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/ipc/gfx_param_traits.h"
+
+// Singly-included section for typedefs.
+#ifndef CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_
+#define CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_
+
+#if defined(OS_WIN)
+// A vector of filters, each being a Tuple containing a display string (i.e.
+// "Text Files") and a filter pattern (i.e. "*.txt").
+typedef std::vector<Tuple<base::string16, base::string16>>
+    GetOpenFileNameFilter;
+#endif  // OS_WIN
+
+#endif  // CHROME_COMMON_CHROME_UTILITY_MESSAGES_H_
+
+#define IPC_MESSAGE_START ChromeUtilityMsgStart
+
+//#if defined(FULL_SAFE_BROWSING)
+//IPC_STRUCT_TRAITS_BEGIN(safe_browsing::zip_analyzer::Results)
+  //IPC_STRUCT_TRAITS_MEMBER(success)
+  //IPC_STRUCT_TRAITS_MEMBER(has_executable)
+  //IPC_STRUCT_TRAITS_MEMBER(has_archive)
+//IPC_STRUCT_TRAITS_END()
+//#endif
+
+#if defined(OS_WIN)
+IPC_STRUCT_BEGIN(ChromeUtilityMsg_GetSaveFileName_Params)
+  IPC_STRUCT_MEMBER(HWND, owner)
+  IPC_STRUCT_MEMBER(DWORD, flags)
+  IPC_STRUCT_MEMBER(GetOpenFileNameFilter, filters)
+  IPC_STRUCT_MEMBER(int, one_based_filter_index)
+  IPC_STRUCT_MEMBER(base::FilePath, suggested_filename)
+  IPC_STRUCT_MEMBER(base::FilePath, initial_directory)
+  IPC_STRUCT_MEMBER(base::string16, default_extension)
+IPC_STRUCT_END()
+#endif  // OS_WIN
+
+//------------------------------------------------------------------------------
+// Utility process messages:
+// These are messages from the browser to the utility process.
+
+// Tell the utility process to parse a JSON string into a Value object.
+IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_ParseJSON,
+                     std::string /* JSON to parse */)
+
+// Tell the utility process to decode the given image data.
+IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_DecodeImage,
+                     std::vector<unsigned char> /* encoded image contents */,
+                     bool /* shrink image if needed for IPC msg limit */)
+
+// Tell the utility process to decode the given JPEG image data with a robust
+// libjpeg codec.
+IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_RobustJPEGDecodeImage,
+                     std::vector<unsigned char>)  // encoded image contents
+
+// Tell the utility process to patch the given |input_file| using |patch_file|
+// and place the output in |output_file|. The patch should use the bsdiff
+// algorithm (Courgette's version).
+IPC_MESSAGE_CONTROL3(ChromeUtilityMsg_PatchFileBsdiff,
+                     base::FilePath /* input_file */,
+                     base::FilePath /* patch_file */,
+                     base::FilePath /* output_file */)
+
+// Tell the utility process to patch the given |input_file| using |patch_file|
+// and place the output in |output_file|. The patch should use the Courgette
+// algorithm.
+IPC_MESSAGE_CONTROL3(ChromeUtilityMsg_PatchFileCourgette,
+                     base::FilePath /* input_file */,
+                     base::FilePath /* patch_file */,
+                     base::FilePath /* output_file */)
+
+#if defined(OS_CHROMEOS)
+// Tell the utility process to create a zip file on the given list of files.
+IPC_MESSAGE_CONTROL3(ChromeUtilityMsg_CreateZipFile,
+                     base::FilePath /* src_dir */,
+                     std::vector<base::FilePath> /* src_relative_paths */,
+                     base::FileDescriptor /* dest_fd */)
+#endif  // defined(OS_CHROMEOS)
+
+// Requests the utility process to respond with a
+// ChromeUtilityHostMsg_ProcessStarted message once it has started.  This may
+// be used if the host process needs a handle to the running utility process.
+IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_StartupPing)
+
+#if defined(FULL_SAFE_BROWSING)
+// Tells the utility process to analyze a zip file for malicious download
+// protection.
+IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_AnalyzeZipFileForDownloadProtection,
+                     IPC::PlatformFileForTransit /* zip_file */)
+#endif
+
+#if defined(OS_WIN)
+// Invokes ui::base::win::OpenFileViaShell from the utility process.
+IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_OpenFileViaShell,
+                     base::FilePath /* full_path */)
+
+// Invokes ui::base::win::OpenFolderViaShell from the utility process.
+IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_OpenFolderViaShell,
+                     base::FilePath /* full_path */)
+
+// Instructs the utility process to invoke GetOpenFileName. |owner| is the
+// parent of the modal dialog, |flags| are OFN_* flags. |filter| constrains the
+// user's file choices. |initial_directory| and |filename| select the directory
+// to be displayed and the file to be initially selected.
+//
+// Either ChromeUtilityHostMsg_GetOpenFileName_Failed or
+// ChromeUtilityHostMsg_GetOpenFileName_Result will be returned when the
+// operation completes whether due to error or user action.
+IPC_MESSAGE_CONTROL5(ChromeUtilityMsg_GetOpenFileName,
+                     HWND /* owner */,
+                     DWORD /* flags */,
+                     GetOpenFileNameFilter /* filter */,
+                     base::FilePath /* initial_directory */,
+                     base::FilePath /* filename */)
+IPC_MESSAGE_CONTROL1(ChromeUtilityMsg_GetSaveFileName,
+                     ChromeUtilityMsg_GetSaveFileName_Params /* params */)
+#endif  // defined(OS_WIN)
+
+//#if defined(OS_ANDROID)
+//// Instructs the utility process to detect support for seccomp-bpf,
+//// and the result is reported through
+//// ChromeUtilityHostMsg_DetectSeccompSupport_Result.
+//IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_DetectSeccompSupport)
+//#endif
+
+//------------------------------------------------------------------------------
+// Utility process host messages:
+// These are messages from the utility process to the browser.
+
+// Reply when the utility process successfully parsed a JSON string.
+//
+// WARNING: The result can be of any Value subclass type, but we can't easily
+// pass indeterminate value types by const object reference with our IPC macros,
+// so we put the result Value into a ListValue. Handlers should examine the
+// first (and only) element of the ListValue for the actual result.
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_ParseJSON_Succeeded,
+                     base::ListValue)
+
+// Reply when the utility process failed in parsing a JSON string.
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_ParseJSON_Failed,
+                     std::string /* error message, if any*/)
+
+// Reply when the utility process has failed while unpacking and parsing a
+// web resource.  |error_message| is a user-readable explanation of what
+// went wrong.
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_UnpackWebResource_Failed,
+                     std::string /* error_message, if any */)
+
+// Reply when the utility process has succeeded in decoding the image.
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DecodeImage_Succeeded,
+                     SkBitmap)  // decoded image
+
+// Reply when an error occurred decoding the image.
+IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_DecodeImage_Failed)
+
+// Reply when a file has been patched.
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_PatchFile_Finished, int /* result */)
+
+//#if defined(OS_CHROMEOS)
+//// Reply when the utility process has succeeded in creating the zip file.
+//IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_CreateZipFile_Succeeded)
+
+//// Reply when an error occured in creating the zip file.
+//IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_CreateZipFile_Failed)
+//#endif  // defined(OS_CHROMEOS)
+
+// Reply when the utility process has started.
+IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_ProcessStarted)
+
+//#if defined(FULL_SAFE_BROWSING)
+//// Reply when a zip file has been analyzed for malicious download protection.
+//IPC_MESSAGE_CONTROL1(
+    //ChromeUtilityHostMsg_AnalyzeZipFileForDownloadProtection_Finished,
+    //safe_browsing::zip_analyzer::Results)
+//#endif
+
+#if defined(OS_WIN)
+IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_GetOpenFileName_Failed)
+IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_GetOpenFileName_Result,
+                     base::FilePath /* directory */,
+                     std::vector<base::FilePath> /* filenames */)
+IPC_MESSAGE_CONTROL0(ChromeUtilityHostMsg_GetSaveFileName_Failed)
+IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_GetSaveFileName_Result,
+                     base::FilePath /* path */,
+                     int /* one_based_filter_index  */)
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_BuildDirectWriteFontCache,
+                     base::FilePath /* cache file path */)
+#endif  // defined(OS_WIN)
+
+//#if defined(OS_ANDROID)
+//// Reply to ChromeUtilityMsg_DetectSeccompSupport to report the level
+//// of kernel support for seccomp-bpf.
+//IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DetectSeccompSupport_ResultPrctl,
+                     //bool [> seccomp prctl supported <])
+//IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_DetectSeccompSupport_ResultSyscall,
+                     //bool [> seccomp syscall supported <])
+//#endif

+ 35 - 0
chromium_src/chrome/common/print_messages.h

@@ -17,6 +17,13 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gfx/geometry/rect.h"
 
+#if defined(OS_WIN)
+#include "ipc/ipc_platform_file.h"
+#include "printing/backend/print_backend.h"
+#include "printing/page_range.h"
+#include "printing/pdf_render_settings.h"
+#endif
+
 #ifndef CHROME_COMMON_PRINT_MESSAGES_H_
 #define CHROME_COMMON_PRINT_MESSAGES_H_
 
@@ -239,3 +246,31 @@ IPC_MESSAGE_ROUTED0(PrintHostMsg_ShowInvalidPrinterSettingsError)
 // Tell the browser printing failed.
 IPC_MESSAGE_ROUTED1(PrintHostMsg_PrintingFailed,
                     int /* document cookie */)
+
+
+#if defined(OS_WIN)
+// Tell the utility process to start rendering the given PDF into a metafile.
+// Utility process would be alive until
+// ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop message.
+IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RenderPDFPagesToMetafiles,
+                     IPC::PlatformFileForTransit, /* input_file */
+                     printing::PdfRenderSettings /* settings */)
+
+// Requests conversion of the next page.
+IPC_MESSAGE_CONTROL2(ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage,
+                     int /* page_number */,
+                     IPC::PlatformFileForTransit /* output_file */)
+
+// Requests utility process to stop conversion and exit.
+IPC_MESSAGE_CONTROL0(ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop)
+
+// Reply when the utility process loaded PDF. |page_count| is 0, if loading
+// failed.
+IPC_MESSAGE_CONTROL1(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount,
+                     int /* page_count */)
+
+// Reply when the utility process rendered the PDF page.
+IPC_MESSAGE_CONTROL2(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone,
+                     bool /* success */,
+                     float /* scale_factor */)
+#endif

+ 144 - 0
chromium_src/chrome/utility/printing_handler.cc

@@ -0,0 +1,144 @@
+// Copyright 2014 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/utility/printing_handler.h"
+
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/path_service.h"
+#include "base/scoped_native_library.h"
+#include "chrome/common/print_messages.h"
+#include "content/public/utility/utility_thread.h"
+#include "pdf/pdf.h"
+#include "printing/page_range.h"
+#include "printing/pdf_render_settings.h"
+
+#if defined(OS_WIN)
+#include "printing/emf_win.h"
+#include "ui/gfx/gdi_util.h"
+#endif
+
+
+namespace {
+
+bool Send(IPC::Message* message) {
+  return content::UtilityThread::Get()->Send(message);
+}
+
+void ReleaseProcessIfNeeded() {
+  content::UtilityThread::Get()->ReleaseProcessIfNeeded();
+}
+
+}  // namespace
+
+PrintingHandler::PrintingHandler() {}
+
+PrintingHandler::~PrintingHandler() {}
+
+bool PrintingHandler::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(PrintingHandler, message)
+#if defined(OS_WIN)
+    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles,
+                        OnRenderPDFPagesToMetafile)
+    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage,
+                        OnRenderPDFPagesToMetafileGetPage)
+    IPC_MESSAGE_HANDLER(ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop,
+                        OnRenderPDFPagesToMetafileStop)
+#endif  // OS_WIN
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
+#if defined(OS_WIN)
+void PrintingHandler::OnRenderPDFPagesToMetafile(
+    IPC::PlatformFileForTransit pdf_transit,
+    const printing::PdfRenderSettings& settings) {
+  pdf_rendering_settings_ = settings;
+  base::File pdf_file = IPC::PlatformFileForTransitToFile(pdf_transit);
+  int page_count = LoadPDF(pdf_file.Pass());
+  //int page_count = 1;
+  Send(
+      new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount(page_count));
+}
+
+void PrintingHandler::OnRenderPDFPagesToMetafileGetPage(
+    int page_number,
+    IPC::PlatformFileForTransit output_file) {
+  base::File emf_file = IPC::PlatformFileForTransitToFile(output_file);
+  float scale_factor = 1.0f;
+  bool success =
+      RenderPdfPageToMetafile(page_number, emf_file.Pass(), &scale_factor);
+  Send(new ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone(
+      success, scale_factor));
+}
+
+void PrintingHandler::OnRenderPDFPagesToMetafileStop() {
+  ReleaseProcessIfNeeded();
+}
+
+int PrintingHandler::LoadPDF(base::File pdf_file) {
+  int64 length64 = pdf_file.GetLength();
+  if (length64 <= 0 || length64 > std::numeric_limits<int>::max())
+    return 0;
+  int length = static_cast<int>(length64);
+
+  pdf_data_.resize(length);
+  if (length != pdf_file.Read(0, pdf_data_.data(), pdf_data_.size()))
+    return 0;
+
+  int total_page_count = 0;
+  if (!chrome_pdf::GetPDFDocInfo(
+          &pdf_data_.front(), pdf_data_.size(), &total_page_count, NULL)) {
+    return 0;
+  }
+  return total_page_count;
+}
+
+bool PrintingHandler::RenderPdfPageToMetafile(int page_number,
+                                              base::File output_file,
+                                              float* scale_factor) {
+  printing::Emf metafile;
+  metafile.Init();
+
+  // We need to scale down DC to fit an entire page into DC available area.
+  // Current metafile is based on screen DC and have current screen size.
+  // Writing outside of those boundaries will result in the cut-off output.
+  // On metafiles (this is the case here), scaling down will still record
+  // original coordinates and we'll be able to print in full resolution.
+  // Before playback we'll need to counter the scaling up that will happen
+  // in the service (print_system_win.cc).
+  *scale_factor =
+      gfx::CalculatePageScale(metafile.context(),
+                              pdf_rendering_settings_.area().right(),
+                              pdf_rendering_settings_.area().bottom());
+  gfx::ScaleDC(metafile.context(), *scale_factor);
+
+  // The underlying metafile is of type Emf and ignores the arguments passed
+  // to StartPage.
+  metafile.StartPage(gfx::Size(), gfx::Rect(), 1);
+  if (!chrome_pdf::RenderPDFPageToDC(
+          &pdf_data_.front(),
+          pdf_data_.size(),
+          page_number,
+          metafile.context(),
+          pdf_rendering_settings_.dpi(),
+          pdf_rendering_settings_.area().x(),
+          pdf_rendering_settings_.area().y(),
+          pdf_rendering_settings_.area().width(),
+          pdf_rendering_settings_.area().height(),
+          true,
+          false,
+          true,
+          true,
+          pdf_rendering_settings_.autorotate())) {
+    return false;
+  }
+  metafile.FinishPage();
+  metafile.FinishDocument();
+  return metafile.SaveTo(&output_file);
+}
+
+#endif  // OS_WIN

+ 59 - 0
chromium_src/chrome/utility/printing_handler.h

@@ -0,0 +1,59 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_UTILITY_PRINTING_HANDLER_H_
+#define CHROME_UTILITY_PRINTING_HANDLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/utility/utility_message_handler.h"
+#include "ipc/ipc_platform_file.h"
+#include "printing/pdf_render_settings.h"
+
+#if !defined(ENABLE_PRINT_PREVIEW) && !defined(OS_WIN)
+#error "Windows or full printing must be enabled"
+#endif
+
+namespace printing {
+class PdfRenderSettings;
+struct PwgRasterSettings;
+struct PageRange;
+}
+
+// Dispatches IPCs for printing.
+class PrintingHandler : public UtilityMessageHandler {
+ public:
+  PrintingHandler();
+  ~PrintingHandler() override;
+
+  // IPC::Listener:
+  bool OnMessageReceived(const IPC::Message& message) override;
+
+ private:
+  // IPC message handlers.
+#if defined(OS_WIN)
+  void OnRenderPDFPagesToMetafile(IPC::PlatformFileForTransit pdf_transit,
+                                  const printing::PdfRenderSettings& settings);
+  void OnRenderPDFPagesToMetafileGetPage(
+      int page_number,
+      IPC::PlatformFileForTransit output_file);
+  void OnRenderPDFPagesToMetafileStop();
+#endif  // OS_WIN
+
+#if defined(OS_WIN)
+  int LoadPDF(base::File pdf_file);
+  bool RenderPdfPageToMetafile(int page_number,
+                               base::File output_file,
+                               float* scale_factor);
+#endif  // OS_WIN
+
+#if defined(OS_WIN)
+  std::vector<char> pdf_data_;
+  printing::PdfRenderSettings pdf_rendering_settings_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(PrintingHandler);
+};
+
+#endif  // CHROME_UTILITY_PRINTING_HANDLER_H_

+ 22 - 0
chromium_src/chrome/utility/utility_message_handler.h

@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+#ifndef CHROME_UTILITY_UTILITY_MESSAGE_HANDLER_H_
+#define CHROME_UTILITY_UTILITY_MESSAGE_HANDLER_H_
+
+namespace IPC {
+class Message;
+}
+
+class UtilityMessageHandler {
+ public:
+  virtual ~UtilityMessageHandler() {}
+
+  // Called when a message is received.  Returns true iff the message was
+  // handled.
+  virtual bool OnMessageReceived(const IPC::Message& message) = 0;
+};
+
+#endif  // CHROME_UTILITY_UTILITY_MESSAGE_HANDLER_H_
+

+ 8 - 0
filenames.gypi

@@ -280,6 +280,8 @@
       'atom/renderer/atom_renderer_client.h',
       'atom/renderer/guest_view_container.cc',
       'atom/renderer/guest_view_container.h',
+      'atom/utility/atom_content_utility_client.cc',
+      'atom/utility/atom_content_utility_client.h',
       'chromium_src/chrome/browser/browser_process.cc',
       'chromium_src/chrome/browser/browser_process.h',
       'chromium_src/chrome/browser/chrome_notification_types.h',
@@ -291,6 +293,8 @@
       'chromium_src/chrome/browser/extensions/global_shortcut_listener_x11.h',
       'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.cc',
       'chromium_src/chrome/browser/extensions/global_shortcut_listener_win.h',
+      'chromium_src/chrome/browser/printing/pdf_to_emf_converter.cc',
+      'chromium_src/chrome/browser/printing/pdf_to_emf_converter.h',
       'chromium_src/chrome/browser/printing/print_job.cc',
       'chromium_src/chrome/browser/printing/print_job.h',
       'chromium_src/chrome/browser/printing/print_job_manager.cc',
@@ -324,6 +328,7 @@
       'chromium_src/chrome/browser/ui/views/color_chooser_aura.h',
       'chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc',
       'chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h',
+      'chromium_src/chrome/common/chrome_utility_messages.h',
       'chromium_src/chrome/common/print_messages.cc',
       'chromium_src/chrome/common/print_messages.h',
       'chromium_src/chrome/common/tts_messages.h',
@@ -338,6 +343,9 @@
       'chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h',
       'chromium_src/chrome/renderer/tts_dispatcher.cc',
       'chromium_src/chrome/renderer/tts_dispatcher.h',
+      'chromium_src/chrome/utility/printing_handler.cc',
+      'chromium_src/chrome/utility/printing_handler.h',
+      'chromium_src/chrome/utility/utility_message_handler.h',
       'chromium_src/library_loaders/libspeechd_loader.cc',
       'chromium_src/library_loaders/libspeechd.h',
       '<@(native_mate_files)',