Bug 1399787 - Part 2.b. Add functions to read/write EMF content from/to a buffer. draft
authorcku <cku@mozilla.com>
Wed, 01 Nov 2017 20:27:17 +0800
changeset 710597 0e17c0219f626351fa3c78e53fbaf4840c01d017
parent 710596 73864ffeb519e67e7fd6a1a41b668398c8406be7
child 710598 c31ab60b22833cd992d50a82f894085223c7fa55
push id92855
push usercku@mozilla.com
push dateMon, 11 Dec 2017 02:44:26 +0000
bugs1399787
milestone59.0a1
Bug 1399787 - Part 2.b. Add functions to read/write EMF content from/to a buffer. With the help of these new function, we can serialize/deserialize EMF content in/out a share memory object. MozReview-Commit-ID: Dm45xEXmMqS
widget/windows/PDFViaEMFPrintHelper.cpp
widget/windows/PDFViaEMFPrintHelper.h
widget/windows/WindowsEMF.cpp
widget/windows/WindowsEMF.h
widget/windows/gtest/TestEMFConversion.cpp
--- a/widget/windows/PDFViaEMFPrintHelper.cpp
+++ b/widget/windows/PDFViaEMFPrintHelper.cpp
@@ -5,16 +5,17 @@
 
 #include "PDFViaEMFPrintHelper.h"
 #include "nsIFileStreams.h"
 #include "WindowsEMF.h"
 #include "nsFileStreams.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/Unused.h"
+#include "mozilla/ipc/ProtocolUtils.h"
 
 /* Scale DC by keeping aspect ratio */
 static
 float ComputeScaleFactor(int aDCWidth, int aDCHeight,
                          int aPageWidth, int aPageHeight)
 {
   MOZ_ASSERT(aPageWidth !=0 && aPageWidth != 0);
 
@@ -181,32 +182,71 @@ PDFViaEMFPrintHelper::DrawPage(HDC aPrin
   return result;
 }
 
 bool
 PDFViaEMFPrintHelper::DrawPageToFile(const wchar_t* aFilePath,
                                      unsigned int aPageIndex,
                                      int aPageWidth, int aPageHeight)
 {
+  MOZ_ASSERT(aFilePath);
+
   // OpenDocument might fail.
   if (!mPDFDoc) {
     MOZ_ASSERT_UNREACHABLE("Make sure OpenDocument return true before"
                            "using DrawPageToFile.");
     return false;
   }
 
   WindowsEMF emf;
   bool result = emf.InitForDrawing(aFilePath);
   NS_ENSURE_TRUE(result, false);
 
   result = RenderPageToDC(emf.GetDC(), aPageIndex, aPageWidth, aPageHeight);
   NS_ENSURE_TRUE(result, false);
   return emf.SaveToFile();
 }
 
+bool
+PDFViaEMFPrintHelper::SavePageToBuffer(unsigned int aPageIndex,
+                                       int aPageWidth, int aPageHeight,
+                                       ipc::Shmem& aMem,
+                                       mozilla::ipc::IShmemAllocator* aAllocator)
+{
+  MOZ_ASSERT(aAllocator);
+
+  // OpenDocument might fail.
+  if (!mPDFDoc) {
+    MOZ_ASSERT_UNREACHABLE("Make sure OpenDocument return true before"
+                           "using DrawPageToFile.");
+    return false;
+  }
+
+  WindowsEMF emf;
+  bool result = emf.InitForDrawing();
+  NS_ENSURE_TRUE(result, false);
+
+  result = RenderPageToDC(emf.GetDC(), aPageIndex, aPageWidth, aPageHeight);
+  NS_ENSURE_TRUE(result, false);
+
+  UINT emfSize = emf.GetEMFContentSize();
+  NS_ENSURE_TRUE(emfSize != 0, false);
+
+  auto shmType = ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
+  result = aAllocator->AllocShmem(emfSize, shmType, &aMem);
+  NS_ENSURE_TRUE(result, false);
+
+  if (!emf.GetEMFContentBits(aMem.get<BYTE>())) {
+    aAllocator->DeallocShmem(aMem);
+    return false;;
+  }
+
+  return true;
+}
+
 void
 PDFViaEMFPrintHelper::CloseDocument()
 {
   if (mPDFDoc) {
     mPDFiumEngine->CloseDocument(mPDFDoc);
     mPDFDoc = nullptr;
   }
 
@@ -216,12 +256,13 @@ PDFViaEMFPrintHelper::CloseDocument()
   }
 }
 
 bool
 PDFViaEMFPrintHelper::CreatePDFiumEngineIfNeed()
 {
   if (!mPDFiumEngine) {
     mPDFiumEngine = PDFiumEngineShim::GetInstanceOrNull();
+    MOZ_ASSERT(mPDFiumEngine);
   }
 
   return !!mPDFiumEngine;
 }
\ No newline at end of file
--- a/widget/windows/PDFViaEMFPrintHelper.h
+++ b/widget/windows/PDFViaEMFPrintHelper.h
@@ -5,24 +5,30 @@
 
 #ifndef PDFVIAEMFPRINTHELPER_H_
 #define PDFVIAEMFPRINTHELPER_H_
 
 #include "nsCOMPtr.h"
 #include "PDFiumEngineShim.h"
 #include "mozilla/Vector.h"
 #include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/ipc/Shmem.h"
 
 /* include windows.h for the HDC definitions that we need. */
 #include <windows.h>
 
 class nsIFile;
 class nsFileInputStream;
 
 namespace mozilla {
+
+namespace ipc {
+  class IShmemAllocator;
+}
+
 namespace widget {
 
 /**
  * This class helps draw a PDF file to a given Windows DC.
  * To do that it first converts the PDF file to EMF.
  * Windows EMF:
  * https://msdn.microsoft.com/en-us/windows/hardware/drivers/print/emf-data-type
  */
@@ -30,33 +36,38 @@ class PDFViaEMFPrintHelper
 {
 public:
   typedef mozilla::ipc::FileDescriptor FileDescriptor;
 
   PDFViaEMFPrintHelper();
   virtual ~PDFViaEMFPrintHelper();
 
   /** Loads the specified PDF file. */
-  NS_IMETHOD OpenDocument(nsIFile *aFile);
+  NS_IMETHOD OpenDocument(nsIFile* aFile);
   NS_IMETHOD OpenDocument(const char* aFileName);
   NS_IMETHOD OpenDocument(const FileDescriptor& aFD);
 
   /** Releases document buffer. */
   void CloseDocument();
 
   int GetPageCount() const { return mPDFiumEngine->GetPageCount(mPDFDoc); }
 
   /** Convert specified PDF page to EMF and draw the EMF onto the given DC. */
   bool DrawPage(HDC aPrinterDC, unsigned int aPageIndex,
                 int aPageWidth, int aPageHeight);
 
-  /** Convert specified PDF page to EMF and save it to file. */
+  /** Convert a specified PDF page to EMF and save it to file. */
   bool DrawPageToFile(const wchar_t* aFilePath, unsigned int aPageIndex,
                       int aPageWidth, int aPageHeight);
 
+  /** Create a share memory and serialize the EMF content into it. */
+  bool SavePageToBuffer(unsigned int aPageIndex, int aPageWidth,
+                        int aPageHeight, ipc::Shmem& aMem,
+                        mozilla::ipc::IShmemAllocator* aAllocator);
+
 protected:
   virtual bool CreatePDFiumEngineIfNeed();
   bool RenderPageToDC(HDC aDC, unsigned int aPageIndex,
                       int aPageWidth, int aPageHeight);
 
   RefPtr<PDFiumEngineShim>    mPDFiumEngine;
   FPDF_DOCUMENT               mPDFDoc;
   PRFileDesc*                 mPrfile;
--- a/widget/windows/WindowsEMF.cpp
+++ b/widget/windows/WindowsEMF.cpp
@@ -35,16 +35,27 @@ WindowsEMF::InitFromFileContents(const w
   MOZ_ASSERT(aMetafilePath);
   ReleaseAllResource();
 
   mEmf = ::GetEnhMetaFileW(aMetafilePath);
   return !!mEmf;
 }
 
 bool
+WindowsEMF::InitFromFileContents(LPBYTE aBytes, UINT aSize)
+{
+  MOZ_ASSERT(aBytes && aSize != 0);
+  ReleaseAllResource();
+
+  mEmf = SetEnhMetaFileBits(aSize, aBytes);
+
+  return !!mEmf;
+}
+
+bool
 WindowsEMF::FinishDocument()
 {
   if (mDC) {
      mEmf = ::CloseEnhMetaFile(mDC);
      mDC = nullptr;
   }
   return !!mEmf;
 }
@@ -63,27 +74,49 @@ WindowsEMF::ReleaseAllResource()
 {
   FinishDocument();
   ReleaseEMFHandle();
 }
 
 bool
 WindowsEMF::Playback(HDC aDeviceContext, const RECT& aRect)
 {
-  if (!FinishDocument()) {
-    return false;
-  }
+  DebugOnly<bool> result = FinishDocument();
+  MOZ_ASSERT(result, "This function should be used after InitXXX.");
 
   return ::PlayEnhMetaFile(aDeviceContext, mEmf, &aRect) != 0;
 }
 
 bool
 WindowsEMF::SaveToFile()
 {
-  if (!FinishDocument()) {
+  DebugOnly<bool> result = FinishDocument();
+  MOZ_ASSERT(result, "This function should be used after InitXXX.");
+
+  ReleaseEMFHandle();
+  return true;
+}
+
+UINT
+WindowsEMF::GetEMFContentSize()
+{
+  DebugOnly<bool> result = FinishDocument();
+  MOZ_ASSERT(result, "This function should be used after InitXXX.");
+
+  return GetEnhMetaFileBits(mEmf, 0, NULL);
+}
+
+bool
+WindowsEMF::GetEMFContentBits(LPBYTE aBytes)
+{
+  DebugOnly<bool> result = FinishDocument();
+  MOZ_ASSERT(result, "This function should be used after InitXXX.");
+
+  UINT emfSize = GetEMFContentSize();
+  if (GetEnhMetaFileBits(mEmf, emfSize, aBytes) != emfSize) {
     return false;
   }
-  ReleaseEMFHandle();
+
   return true;
 }
 
 } // namespace widget
 } // namespace mozilla
\ No newline at end of file
--- a/widget/windows/WindowsEMF.h
+++ b/widget/windows/WindowsEMF.h
@@ -39,16 +39,24 @@ public:
 
   /**
    * Initializes the object with an existing EMF file. Consumers cannot use
    * GetDC() to obtain an HDC to modify the file. They can only use Playback().
    */
   bool InitFromFileContents(const wchar_t* aMetafilePath);
 
   /**
+   * Creates the EMF from the specified data
+   *
+   * @param aByte Pointer to a buffer that contains EMF data.
+   * @param aSize Specifies the size, in bytes, of aByte.
+   */
+  bool InitFromFileContents(PBYTE aBytes, UINT aSize);
+
+  /**
    * If this object was initiaziled using InitForDrawing() then this function
    * returns an HDC that can be drawn to generate the EMF output. Otherwise it
    * returns null. After finishing with the HDC, consumers could call Playback()
    * to draw EMF onto the given DC or call SaveToFile() to finish writing the
    * EMF file.
    */
   HDC GetDC() const
   {
@@ -65,16 +73,28 @@ public:
 
   /**
    * Called to generate the EMF file once a consumer has finished drawing to
    * the HDC returned by GetDC(), if initializes the object with the path of a
    * file.
    */
   bool SaveToFile();
 
+  /**
+   * Return the size of the enhanced metafile, in bytes.
+   */
+  UINT GetEMFContentSize();
+
+  /**
+   * Retrieves the contents of the EMF and copies them into a buffer.
+   *
+   * @param aByte the buffer to receive the data.
+   */
+  bool GetEMFContentBits(PBYTE aBytes);
+
 private:
 
   WindowsEMF(const WindowsEMF& aEMF) = delete;
   bool FinishDocument();
   void ReleaseEMFHandle();
   void ReleaseAllResource();
 
   /* Compiled EMF data handle. */
--- a/widget/windows/gtest/TestEMFConversion.cpp
+++ b/widget/windows/gtest/TestEMFConversion.cpp
@@ -190,15 +190,15 @@ TEST(TestEMFConversion, TestInsufficient
   ASSERT_TRUE(SetupPrintHelper("PrinterTestPage.pdf", PDFHelper.get()));
 
   nsAutoString emfPath;
   ASSERT_TRUE(NS_SUCCEEDED(GetFilePathViaSpecialDirectory(NS_OS_TEMP_DIR,
                                                          "gtest.emf",
                                                           emfPath)));
 
   ASSERT_FALSE(PDFHelper->DrawPageToFile(emfPath.get(), 0, 0, 0));
-  ASSERT_FALSE(PDFHelper->DrawPageToFile(emfPath.get(), 0, 100, -1));
+  ASSERT_FALSE(PDFHelper->PageToFile(emfPath.get(), 0, 100, -1));
 
   PDFHelper->CloseDocument();
 }
 
 } // namespace widget
 } // namespace mozilla
\ No newline at end of file