Bug 1399787 - Part 10. Make PrintTargetEMF use the PDFium process to convert to EMF. draft
authorcku <cku@mozilla.com>
Sun, 05 Nov 2017 03:06:04 +0800
changeset 710606 1da7c93de088dfc746f05ae33ad41610292c06d0
parent 710605 6ee4003807cbcf7e0a5b7df83471df2e38a3599a
child 710607 a5b102aafbc368f7550338d51ce1848ada4cb299
push id92855
push usercku@mozilla.com
push dateMon, 11 Dec 2017 02:44:26 +0000
bugs1399787
milestone59.0a1
Bug 1399787 - Part 10. Make PrintTargetEMF use the PDFium process to convert to EMF. We integrate PrintTargetEMF with the PDFium process to convert PDF into EMF in this patch. MozReview-Commit-ID: 5F0setrL94n
gfx/thebes/PrintTargetEMF.cpp
gfx/thebes/PrintTargetEMF.h
gfx/thebes/moz.build
widget/windows/PDFiumChild.cpp
widget/windows/PDFiumChild.h
widget/windows/PDFiumParent.cpp
widget/windows/PDFiumParent.h
widget/windows/PDFiumProcessParent.cpp
widget/windows/PDFiumProcessParent.h
widget/windows/moz.build
--- a/gfx/thebes/PrintTargetEMF.cpp
+++ b/gfx/thebes/PrintTargetEMF.cpp
@@ -2,18 +2,23 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PrintTargetEMF.h"
 #include "nsAnonymousTemporaryFile.h"
 #include "nsIFile.h"
 #include "mozilla/widget/PDFiumProcessParent.h"
+#include "mozilla/widget/PDFiumParent.h"
+#include "mozilla/widget/WindowsEMF.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "private/pprio.h"
 
 using mozilla::gfx::DrawTarget;
+using mozilla::ipc::FileDescriptor;
 
 namespace mozilla {
 namespace gfx {
 
 PrintTargetEMF::PrintTargetEMF(HDC aDC, const IntSize& aSize)
   : PrintTarget(/* not using cairo_surface_t */ nullptr, aSize)
   , mPDFiumProcess(nullptr)
   , mPrinterDC(aDC)
@@ -56,17 +61,17 @@ PrintTargetEMF::BeginPrinting(const nsAS
   docinfo.lpszDocName = titleStr.Length() > 0 ? titleStr.get() : L"Mozilla Document";
   docinfo.lpszOutput = docName.Length() > 0 ? docName.get() : nullptr;
   docinfo.lpszDatatype = nullptr;
   docinfo.fwType = 0;
 
   ::StartDocW(mPrinterDC, &docinfo);
 
   mPDFiumProcess = new PDFiumProcessParent();
-  NS_ENSURE_TRUE(mPDFiumProcess->Launch(), NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(mPDFiumProcess->Launch(this), NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
 PrintTargetEMF::EndPrinting()
 {
   return (::EndDoc(mPrinterDC) <= 0) ? NS_ERROR_FAILURE : NS_OK;
@@ -107,23 +112,25 @@ PrintTargetEMF::BeginPage()
 nsresult
 PrintTargetEMF::EndPage()
 {
   mTargetForCurrentPage->EndPage();
   mTargetForCurrentPage->EndPrinting();
   mTargetForCurrentPage->Finish();
   mTargetForCurrentPage = nullptr;
 
-  // TODO: pass mPDFFileForOnePage to the PDFium process.
-
-  mPDFFileForOnePage->Remove(/* aRecursive */ false);
-  mPDFFileForOnePage = nullptr;
-
-  // TODO: we should call EndPage(mPrinterDC), but not here. We should call it
-  // after the PDFium process calls Send ConvertToEMFDone.
+  PRFileDesc* prfile;
+  nsresult rv = mPDFFileForOnePage->OpenNSPRFileDesc(PR_RDONLY, PR_IRWXU,
+                                                     &prfile);
+  NS_ENSURE_SUCCESS(rv, rv);
+  FileDescriptor descriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfile)));
+  mPDFiumProcess->GetActor()->SendConvertToEMF(descriptor,
+                                        ::GetDeviceCaps(mPrinterDC, HORZRES),
+                                        ::GetDeviceCaps(mPrinterDC, VERTRES));
+  PR_Close(prfile);
 
   return NS_OK;
 }
 
 already_AddRefed<DrawTarget>
 PrintTargetEMF::MakeDrawTarget(const IntSize& aSize,
                                DrawEventRecorder* aRecorder)
 {
@@ -140,10 +147,37 @@ PrintTargetEMF::GetReferenceDrawTarget(D
 
   if (!mRefDT) {
     mRefDT = mRefTarget->GetReferenceDrawTarget(aRecorder);
   }
 
   return mRefDT.forget();
 }
 
+void
+PrintTargetEMF::ConvertToEMFDone(const nsresult& aResult,
+                                 mozilla::ipc::Shmem&& aEMF)
+{
+  MOZ_ASSERT_IF(NS_FAILED(aResult), aEMF.Size<uint8_t>() == 0);
+
+  if (NS_SUCCEEDED(aResult)) {
+    if (::StartPage(mPrinterDC) > 0) {
+      mozilla::widget::WindowsEMF emf;
+      emf.InitFromFileContents(aEMF.get<BYTE>(), aEMF.Size<BYTE>());
+      RECT printRect = {0, 0, ::GetDeviceCaps(mPrinterDC, HORZRES),
+                        ::GetDeviceCaps(mPrinterDC, VERTRES)};
+      DebugOnly<bool> ret = emf.Playback(mPrinterDC, printRect);
+      MOZ_ASSERT(ret);
+
+      ::EndPage(mPrinterDC);
+    }
+
+    mPDFiumProcess->GetActor()->DeallocShmem(aEMF);
+  }
+
+  mPDFFileForOnePage->Remove(/* aRecursive */ false);
+  mPDFFileForOnePage = nullptr;
+
+  // TBD: We should call RemotePrintJobChild::SendPageProcessed here.
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/thebes/PrintTargetEMF.h
+++ b/gfx/thebes/PrintTargetEMF.h
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_PRINTTARGETEMF_H
 #define MOZILLA_GFX_PRINTTARGETEMF_H
 
 #include "PrintTargetSkPDF.h"
+#include "mozilla/ipc/Shmem.h"
 
 /* include windows.h for the HDC definitions that we need. */
 #include <windows.h>
 
 namespace mozilla {
 namespace widget {
   class PDFiumProcessParent;
 }
@@ -48,16 +49,18 @@ public:
 
   already_AddRefed<DrawTarget>
   MakeDrawTarget(const IntSize& aSize,
                  DrawEventRecorder* aRecorder = nullptr) final;
 
   already_AddRefed<DrawTarget>
   GetReferenceDrawTarget(DrawEventRecorder* aRecorder) final;
 
+  void ConvertToEMFDone(const nsresult& aResult, mozilla::ipc::Shmem&& aEMF);
+
 private:
   PrintTargetEMF(HDC aDC, const IntSize& aSize);
   ~PrintTargetEMF() override;
 
   nsString mTitle;
   RefPtr<PrintTargetSkPDF> mTargetForCurrentPage;
   nsCOMPtr<nsIFile>        mPDFFileForOnePage;
   RefPtr<PrintTargetSkPDF> mRefTarget;
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -212,16 +212,19 @@ UNIFIED_SOURCES += [
     'VsyncSource.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     UNIFIED_SOURCES += [
         'gfxMacPlatformFontList.mm',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    EXPORTS.mozilla.gfx += [
+        'PrintTargetEMF.h',
+    ]
     UNIFIED_SOURCES += [
         'D3D11Checks.cpp',
         'DeviceManagerDx.cpp',
         'PrintTargetEMF.cpp',
     ]
 
 if CONFIG['MOZ_ENABLE_SKIA_PDF']:
     EXPORTS.mozilla.gfx += [
--- a/widget/windows/PDFiumChild.cpp
+++ b/widget/windows/PDFiumChild.cpp
@@ -32,18 +32,36 @@ PDFiumChild::Init(base::ProcessId aParen
 
 mozilla::ipc::IPCResult
 PDFiumChild::RecvConvertToEMF(const FileDescriptor& aFD,
                               const int& aPageWidth,
                               const int& aPageHeight)
 {
   MOZ_ASSERT(aFD.IsValid() && aPageWidth != 0 && aPageHeight != 0);
 
-  // TBD: Initiate PDFium library.
+  ipc::Shmem smem;
+  PDFViaEMFPrintHelper convertor;
+  if (NS_FAILED(convertor.OpenDocument(aFD))) {
+    Unused << SendConvertToEMFDone(NS_ERROR_FAILURE, smem);
+    return IPC_OK();
+  }
+
+  MOZ_ASSERT(convertor.GetPageCount() == 1, "we assume each given PDF contains"                                        "one page only");
 
+  if (!convertor.SavePageToBuffer(0, aPageWidth, aPageHeight, smem, this)) {
+    Unused << SendConvertToEMFDone(NS_ERROR_FAILURE, smem);
+    return IPC_OK();
+  }
+
+  if (!smem.IsReadable()) {
+    Unused << SendConvertToEMFDone(NS_ERROR_FAILURE, smem);
+    return IPC_OK();
+  }
+
+  Unused << SendConvertToEMFDone(NS_OK, smem);
   return IPC_OK();
 }
 
 void
 PDFiumChild::OnChannelConnected(int32_t pid)
 {
   SetOtherProcessId(pid);
 }
--- a/widget/windows/PDFiumChild.h
+++ b/widget/windows/PDFiumChild.h
@@ -6,25 +6,29 @@
 #ifndef PDFIUMCHILD_H_
 #define PDFIUMCHILD_H_
 
 #include "mozilla/widget/PPDFiumChild.h"
 
 namespace mozilla {
 namespace widget {
 
-class PDFiumChild final : public PPDFiumChild {
+class PDFiumChild final : public PPDFiumChild,
+                          public mozilla::ipc::IShmemAllocator
+{
 public:
   PDFiumChild();
   virtual ~PDFiumChild();
 
   bool Init(base::ProcessId aParentPid,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
 
+  FORWARD_SHMEM_ALLOCATOR_TO(PPDFiumChild)
+
 private:
   // PPDFiumChild functions.
   mozilla::ipc::IPCResult RecvConvertToEMF(const FileDescriptor& aFD,
                                            const int& aPageWidth,
                                            const int& aPageHeight) override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void OnChannelConnected(int32_t pid) override;
 };
--- a/widget/windows/PDFiumParent.cpp
+++ b/widget/windows/PDFiumParent.cpp
@@ -1,20 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PDFiumParent.h"
 #include "nsDeviceContextSpecWin.h"
+#include "mozilla/gfx/PrintTargetEMF.h"
 
 namespace mozilla {
 namespace widget {
 
-PDFiumParent::PDFiumParent()
+PDFiumParent::PDFiumParent(PrintTargetEMF* aTarget)
+  : mTarget(aTarget)
 {
 }
 
 bool
 PDFiumParent::Init(IPC::Channel* aChannel, base::ProcessId aPid)
 {
   if (NS_WARN_IF(!Open(aChannel, aPid))) {
     return false;
@@ -29,17 +31,17 @@ PDFiumParent::ActorDestroy(ActorDestroyR
 {
 }
 
 mozilla::ipc::IPCResult
 PDFiumParent::RecvConvertToEMFDone(const nsresult& aResult,
                                    mozilla::ipc::Shmem&& aEMFContents)
 {
   MOZ_ASSERT(aEMFContents.IsReadable());
-  // TBD: playback aEMFContents onto printer DC.
+  mTarget->ConvertToEMFDone(aResult, Move(aEMFContents));
 
   return IPC_OK();
 }
 
 void
 PDFiumParent::OnChannelConnected(int32_t pid)
 {
   SetOtherProcessId(pid);
--- a/widget/windows/PDFiumParent.h
+++ b/widget/windows/PDFiumParent.h
@@ -3,36 +3,47 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef PDFIUMPARENT_H_
 #define PDFIUMPARENT_H_
 
 #include "mozilla/widget/PPDFiumParent.h"
 
+namespace mozilla {
+namespace gfx {
+  class PrintTargetEMF;
+}
+}
 
 namespace mozilla {
 namespace widget {
 
-class PDFiumParent final : public PPDFiumParent {
-public:
+class PDFiumParent final : public PPDFiumParent,
+                           public mozilla::ipc::IShmemAllocator
+{
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PDFiumParent)
 
-  explicit PDFiumParent();
+  typedef mozilla::gfx::PrintTargetEMF PrintTargetEMF;
+
+  explicit PDFiumParent(PrintTargetEMF* aTarget);
 
   bool Init(IPC::Channel* aChannel, base::ProcessId aPid);
 
+  FORWARD_SHMEM_ALLOCATOR_TO(PPDFiumParent)
 private:
   ~PDFiumParent() {}
 
   // PPDFiumParent functions.
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   mozilla::ipc::IPCResult RecvConvertToEMFDone(const nsresult& aResult,
                                                mozilla::ipc::Shmem&& aEMFContents) override;
   void OnChannelConnected(int32_t pid) override;
   void DeallocPPDFiumParent() override;
+
+  PrintTargetEMF* mTarget;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // PDFIUMPARENT_H_
--- a/widget/windows/PDFiumProcessParent.cpp
+++ b/widget/windows/PDFiumProcessParent.cpp
@@ -28,27 +28,27 @@ PDFiumProcessParent::~PDFiumProcessParen
   MOZ_COUNT_DTOR(PDFiumProcessParent);
 
   if (mPDFiumParentActor) {
     mPDFiumParentActor->Close();
   }
 }
 
 bool
-PDFiumProcessParent::Launch()
+PDFiumProcessParent::Launch(PrintTargetEMF* aTarget)
 {
   mLaunchThread = NS_GetCurrentThread();
 
   if (!SyncLaunch()) {
     return false;
   }
 
   // Open the top level protocol for PDFium process.
   MOZ_ASSERT(!mPDFiumParentActor);
-  mPDFiumParentActor = new PDFiumParent();
+  mPDFiumParentActor = new PDFiumParent(aTarget);
   return mPDFiumParentActor->Init(GetChannel(),
                             base::GetProcId(GetChildProcessHandle()));
 }
 
 void
 PDFiumProcessParent::Delete()
 {
   // PDFiumProcessParent::Launch is not called, protocol is not created.
--- a/widget/windows/PDFiumProcessParent.h
+++ b/widget/windows/PDFiumProcessParent.h
@@ -11,36 +11,42 @@
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 
 class nsIRunnable;
 class nsDeviceContextSpecWin;
 
 #ifdef MOZ_ENABLE_SKIA_PDF
 namespace mozilla {
 namespace widget {
-class PDFiumParent;
+  class PDFiumParent;
+}
+namespace gfx {
+  class PrintTargetEMF;
 }
 }
 #endif
 
 namespace mozilla {
 namespace widget {
 
 class PDFiumProcessParent final : public mozilla::ipc::GeckoChildProcessHost
 {
 public:
+  typedef mozilla::gfx::PrintTargetEMF PrintTargetEMF;
+
   PDFiumProcessParent();
   ~PDFiumProcessParent();
 
-  bool Launch();
+  bool Launch(PrintTargetEMF* aTarget);
 
   void Delete();
 
   bool CanShutdown() override { return true; }
 
+  PDFiumParent* GetActor() const { return mPDFiumParentActor; }
 private:
 
   DISALLOW_COPY_AND_ASSIGN(PDFiumProcessParent);
 
   RefPtr<PDFiumParent> mPDFiumParentActor;
   nsCOMPtr<nsIThread> mLaunchThread;
 };
 
--- a/widget/windows/moz.build
+++ b/widget/windows/moz.build
@@ -25,16 +25,17 @@ EXPORTS.mozilla.widget += [
     'InProcessWinCompositorWidget.h',
     'PDFiumChild.h',
     'PDFiumEngineShim.h',
     'PDFiumParent.h',
     'PDFiumProcessChild.h',
     'PDFiumProcessParent.h',
     'PDFViaEMFPrintHelper.h',
     'WinCompositorWidget.h',
+    'WindowsEMF.h',
     'WinMessages.h',
     'WinModifierKeyState.h',
     'WinNativeEventData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AudioSession.cpp',
     'CompositorWidgetChild.cpp',