Bug 1382512 - Convert the EMF to the bitmap and compare the bitmap content with reference draft
authorFarmer Tseng <fatseng@mozilla.com>
Fri, 04 Aug 2017 15:26:14 +0800
changeset 621026 646845effdce5dae25ec71ee0b0f3c0f520569bb
parent 621025 4f958617cc6e260b1b4aa1d4d92855584067b7c1
child 640875 43fe43a2fb24c26298f64752bbc27919dc0ff06b
push id72231
push userbmo:fatseng@mozilla.com
push dateFri, 04 Aug 2017 07:41:57 +0000
bugs1382512
milestone56.0a1
Bug 1382512 - Convert the EMF to the bitmap and compare the bitmap content with reference MozReview-Commit-ID: 68O0iR91p4f
widget/windows/gtest/TestEMFConversion.cpp
--- a/widget/windows/gtest/TestEMFConversion.cpp
+++ b/widget/windows/gtest/TestEMFConversion.cpp
@@ -133,10 +133,301 @@ TEST(TestEMFConversion, TestInsufficient
   pageHeight = -1;
   result = PDFHelper->DrawPageToFile(emfPath.get(), 0,
                                      pageWidth, pageHeight);
   ASSERT_FALSE(result);
 
   PDFHelper->CloseDocument();
 }
 
+class EMFtoBitmapHelper
+{
+public:
+  EMFtoBitmapHelper();
+  ~EMFtoBitmapHelper();
+
+  // Convert EMF to bitmap and retrive bitmap content
+  DWORD GetBitmapViaEMF(nsAutoString aEMFFile, Vector<char>& aBitmapBuf);
+
+private:
+  bool CalculateWidthAndHeight(HENHMETAFILE aEmf,
+                               LONG& aWidth, LONG& aHeight);
+  bool SetWhiteColorToMemoryDC();
+  DWORD GetBitmap(Vector<char>& aBitmap);
+
+  HDC mDC;
+  HDC mMemDC;
+  HBITMAP mBitmap;
+};
+
+EMFtoBitmapHelper::EMFtoBitmapHelper()
+  : mDC(NULL)
+  , mMemDC(NULL)
+  , mBitmap(NULL)
+{
+}
+
+EMFtoBitmapHelper::~EMFtoBitmapHelper()
+{
+  if (mBitmap) {
+    ::DeleteObject(mBitmap);
+  }
+
+  if (mMemDC) {
+    ::DeleteObject(mMemDC);
+  }
+
+  if (mDC) {
+    ::ReleaseDC(NULL, mDC);
+  }
+}
+
+bool
+EMFtoBitmapHelper::SetWhiteColorToMemoryDC()
+{
+  if (mMemDC == NULL) {
+    return false;
+  }
+
+  // Set brush to desired background color
+  HBRUSH backBrush= reinterpret_cast<HBRUSH>(RGB(255, 255, 255));
+  // Save old brush
+  HBRUSH oldBrush = reinterpret_cast<HBRUSH>(::SelectObject(mMemDC, backBrush));
+
+  RECT rect;
+  int clipBoxType = ::GetClipBox(mMemDC, &rect);
+  if (clipBoxType == ERROR) {
+    return false;
+  }
+
+  // paint the given rectangle using the brush that is currently selected
+  // into the specified device context
+  if (!::PatBlt(mMemDC, rect.left, rect.top, abs(rect.left - rect.right),
+                abs(rect.top-rect.bottom ), PATCOPY)) {
+    return false;
+  }
+
+  // Select back the old brush
+  ::SelectObject(mMemDC, oldBrush);
+  return true;
+}
+
+bool
+EMFtoBitmapHelper::CalculateWidthAndHeight(HENHMETAFILE aEmf,
+                                           LONG& aWidth, LONG& aHeight)
+{
+  if (aEmf == NULL || mMemDC == NULL) {
+    return false;
+  }
+
+  // Get the header from the enhanced metafile.
+  ENHMETAHEADER	emh;
+  ZeroMemory(&emh, sizeof(ENHMETAHEADER));
+  emh.nSize = sizeof(ENHMETAHEADER);
+  if(::GetEnhMetaFileHeader(aEmf, sizeof(ENHMETAHEADER), &emh) == 0 ) {
+    return false;
+  }
+
+  // Get the characteristics of the output device.
+  int pixelsX = ::GetDeviceCaps(mMemDC, HORZRES);
+  int pixelsY = ::GetDeviceCaps(mMemDC, VERTRES);
+  int mmX = ::GetDeviceCaps(mMemDC, HORZSIZE);
+  int mmY = ::GetDeviceCaps(mMemDC, VERTSIZE);
+
+  // The metafile intended dimension is given 0.01 mm units. But, the units of
+  // bitmap dimension are pixels. Therefore, we need to convert those to device
+  // units on the target device.
+  LONG top = static_cast<LONG>(emh.rclFrame.top * pixelsY / (mmY * 100.0f));
+  LONG left = static_cast<LONG>(emh.rclFrame.left * pixelsX / (mmX * 100.0f));
+  LONG right = static_cast<LONG>(emh.rclFrame.right * pixelsX / (mmX * 100.0f));
+  LONG bottom = static_cast<LONG>(emh.rclFrame.bottom * pixelsY /
+                                  (mmY * 100.0f));
+
+  // Calculate the width and height of the metafile
+  aWidth = abs(left - right);
+  aHeight = abs(top - bottom);
+
+  return true;
+}
+
+DWORD
+EMFtoBitmapHelper::GetBitmapViaEMF(nsAutoString aEMFFile, Vector<char>& aBitmapBuf)
+{
+  if (mDC == NULL) {
+    mDC = ::GetDC(NULL);
+    if (mDC == NULL) {
+      return 0;
+    }
+  }
+
+  // Create a Memory DC compatible to WindowDC
+  if (mMemDC == NULL) {
+    mMemDC = ::CreateCompatibleDC(mDC);
+    if (mMemDC == NULL) {
+      return 0;
+    }
+  }
+
+  // Get the Handle from the enhanced metafile
+  HENHMETAFILE hEMF = ::GetEnhMetaFileW(aEMFFile.get());
+  if (hEMF == NULL) {
+    return 0;
+  }
+
+  // Calculate the width and height of the metafile
+  LONG width;
+  LONG height;
+  if (!CalculateWidthAndHeight(hEMF, width, height)) {
+    ::DeleteEnhMetaFile(hEMF);
+    return 0;
+  }
+  RECT	rect = {0, 0, width, height};
+
+  // Create a bitmap compatible
+  if (mBitmap) {
+    ::DeleteObject(mBitmap);
+  }
+  mBitmap = ::CreateCompatibleBitmap(mDC, width, height);
+  if (mBitmap == NULL) {
+    ::DeleteEnhMetaFile(hEMF);
+    return 0;
+  }
+
+  // Select the bitmap into the Mem DC
+  HBITMAP oldBitmap = reinterpret_cast<HBITMAP>(::SelectObject(mMemDC, mBitmap));
+
+  // Paint the background of the DC to White
+  if (!SetWhiteColorToMemoryDC()) {
+    ::DeleteEnhMetaFile(hEMF);
+    return 0;
+  }
+
+  // Now play the enhanced metafile into the memory DC;
+  bool result = ::PlayEnhMetaFile(mMemDC, hEMF, &rect);
+  ::DeleteEnhMetaFile(hEMF);
+
+  // Select back the old bitmap
+  ::SelectObject(mMemDC, oldBitmap);
+
+  if (!result) {
+    return 0;
+  }
+
+  return GetBitmap(aBitmapBuf);
+}
+
+DWORD
+EMFtoBitmapHelper::GetBitmap(Vector<char>& aBitmapBuf)
+{
+  if (mDC == NULL || mBitmap == NULL) {
+    return 0;
+  }
+
+  BITMAPINFO bitmapInfo = {0};
+
+  bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  if (!::GetDIBits(mDC, mBitmap, 0, 0, NULL,
+                   &bitmapInfo, DIB_RGB_COLORS)) {
+    return 0;
+  }
+
+  if (!aBitmapBuf.initCapacity(bitmapInfo.bmiHeader.biSizeImage)) {
+    return 0;
+  }
+
+  // calculate the size in BYTEs of the additional
+  // memory needed for the bmiColor table
+  int AdditionalMemory = 0;
+  switch (bitmapInfo.bmiHeader.biBitCount) {
+    case 1:
+      AdditionalMemory = 1 * sizeof(RGBQUAD);
+      break;
+    case 4:
+      AdditionalMemory = 15 * sizeof(RGBQUAD);
+      break;
+    case 8:
+      AdditionalMemory = 255 * sizeof(RGBQUAD);
+      break;
+  }
+
+  // we have to allocate room for the bmiColor table that will be
+  // attached to our BITMAPINFO variables
+  Vector<char> tempBuf;
+  if (!tempBuf.initCapacity(sizeof(BITMAPINFO) + AdditionalMemory)) {
+    return 0;
+  }
+  memset(tempBuf.begin(), 0, sizeof(BITMAPINFO) + AdditionalMemory);
+  memcpy(tempBuf.begin(), &bitmapInfo, sizeof(BITMAPINFO));
+  PBITMAPINFO pBitmapInfoLeft = reinterpret_cast<PBITMAPINFO>(tempBuf.begin());
+
+  // zero out the bitmap bit buffers
+  memset(aBitmapBuf.begin(), 0, bitmapInfo.bmiHeader.biSizeImage);
+  if (!::GetDIBits(mDC, mBitmap, 0,
+                   pBitmapInfoLeft->bmiHeader.biHeight,
+                   aBitmapBuf.begin(),
+                   pBitmapInfoLeft, DIB_RGB_COLORS)) {
+    return 0;
+  }
+
+  return bitmapInfo.bmiHeader.biSizeImage;
+}
+
+TEST(TestEMFConversion, CompareEMFbyBitmap)
+{
+  UniquePtr<PDFViaEMFPrintHelper> PDFHelper =
+    CreatePrintHelper("PrinterTestPage.pdf");
+  ASSERT_NE(PDFHelper, nullptr);
+
+  // Convert a PDF file to an EMF file(PrinterTestPage.pdf -> gtest.emf)
+  nsAutoString emfPath;
+  nsresult rv = GetFilePathViaSpecialDirectory(NS_OS_TEMP_DIR,
+                                               "gtest.emf",
+                                               emfPath);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  int pageWidth = 4961;
+  int pageHeight = 7016;
+  bool result = PDFHelper->DrawPageToFile(emfPath.get(), 0,
+                                          pageWidth, pageHeight);
+  ASSERT_TRUE(result);
+
+  PDFHelper->CloseDocument();
+
+  // Check the EMF file size if it is great than zero
+  RefPtr<nsLocalFile> savedFile = new nsLocalFile();
+  savedFile->InitWithPath(emfPath);
+  RefPtr<nsFileInputStream> inputStream = new nsFileInputStream();
+  rv = inputStream->Init(savedFile,
+                         /* ioFlags */ PR_RDONLY,
+                         /* perm */ -1,
+                         /* behaviorFlags */ 0);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  int64_t size = 0;
+  inputStream->GetSize(&size);
+  ASSERT_GT(size, 0);
+
+  // Get the reference file, PrinterTestPage_ref.emf.
+  nsAutoString emfPathRef;
+  rv = GetFilePathViaSpecialDirectory(NS_OS_CURRENT_WORKING_DIR,
+                                      "PrinterTestPage_ref.emf",
+                                      emfPathRef);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  UniquePtr<EMFtoBitmapHelper> bitmapHelper1 =
+    MakeUnique<EMFtoBitmapHelper>();
+
+  Vector<char> bitmap1;
+  DWORD bitmapSize1 = bitmapHelper1->GetBitmapViaEMF(emfPath, bitmap1);
+
+  UniquePtr<EMFtoBitmapHelper> bitmapHelper2 =
+    MakeUnique<EMFtoBitmapHelper>();
+
+  Vector<char> bitmap2;
+  DWORD bitmapSize2 = bitmapHelper2->GetBitmapViaEMF(emfPathRef, bitmap2);
+
+  ASSERT_TRUE((bitmapSize1 != 0) && (bitmapSize1 == bitmapSize2));
+  ASSERT_TRUE(memcmp(bitmap1.begin(), bitmap2.begin(),
+                     bitmapSize1) == 0);
+}
+
 } // namespace widget
 } // namespace mozilla
\ No newline at end of file