forked from mirrors/gecko-dev
		
	Reuse the code in PDFViaEMFPrintHelper. MozReview-Commit-ID: 3NAVxuv2jJH --HG-- extra : rebase_source : 059efda5fc232ec741557b32e6956a58dff76691
		
			
				
	
	
		
			189 lines
		
	
	
		
			No EOL
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			No EOL
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 | 
						|
 * 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 "PDFViaEMFPrintHelper.h"
 | 
						|
#include "nsIFileStreams.h"
 | 
						|
#include "WindowsEMF.h"
 | 
						|
#include "nsFileStreams.h"
 | 
						|
#include "mozilla/UniquePtrExtensions.h"
 | 
						|
#include "mozilla/dom/File.h"
 | 
						|
#include "mozilla/Unused.h"
 | 
						|
 | 
						|
/* Scale DC by keeping aspect ratio */
 | 
						|
static
 | 
						|
float ComputeScaleFactor(int aDCWidth, int aDCHeight,
 | 
						|
                         int aPageWidth, int aPageHeight)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aPageWidth !=0 && aPageWidth != 0);
 | 
						|
 | 
						|
  return (aDCWidth >= aPageWidth && aDCHeight >= aPageHeight)
 | 
						|
    ? 1.0 /* If page fits DC - no scaling needed. */
 | 
						|
    : std::min(static_cast<float>(aDCWidth) / static_cast<float>(aPageWidth),         static_cast<float>(aDCHeight) / static_cast<float>(aPageHeight));
 | 
						|
}
 | 
						|
 | 
						|
PDFViaEMFPrintHelper::PDFViaEMFPrintHelper()
 | 
						|
  : mPDFDoc(nullptr)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
PDFViaEMFPrintHelper::~PDFViaEMFPrintHelper()
 | 
						|
{
 | 
						|
  CloseDocument();
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
PDFViaEMFPrintHelper::OpenDocument(nsIFile *aFile)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aFile);
 | 
						|
 | 
						|
  if (mPDFDoc) {
 | 
						|
    MOZ_ASSERT_UNREACHABLE("We can only open one PDF at a time,"
 | 
						|
                           "Use CloseDocument() to close the opened file"
 | 
						|
                           "before calling OpenDocument()");
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  nsAutoCString nativePath;
 | 
						|
  nsresult rv = aFile->GetNativePath(nativePath);
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    return rv;
 | 
						|
  }
 | 
						|
 | 
						|
  return OpenDocument(nativePath.get());
 | 
						|
}
 | 
						|
 | 
						|
nsresult
 | 
						|
PDFViaEMFPrintHelper::OpenDocument(const char* aFileName)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aFileName);
 | 
						|
 | 
						|
  if (mPDFDoc) {
 | 
						|
    MOZ_ASSERT_UNREACHABLE("We can only open one PDF at a time,"
 | 
						|
                           "Use CloseDocument() to close the opened file"
 | 
						|
                           "before calling OpenDocument()");
 | 
						|
    return NS_ERROR_FAILURE;
 | 
						|
  }
 | 
						|
 | 
						|
  NS_ENSURE_TRUE(CreatePDFiumEngineIfNeed(), NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  mPDFDoc = mPDFiumEngine->LoadDocument(aFileName, nullptr);
 | 
						|
  NS_ENSURE_TRUE(mPDFDoc, NS_ERROR_FAILURE);
 | 
						|
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PDFViaEMFPrintHelper::RenderPageToDC(HDC aDC, unsigned int aPageIndex,
 | 
						|
                                     int aPageWidth, int aPageHeight)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aDC && mPDFDoc);
 | 
						|
  MOZ_ASSERT(static_cast<int>(aPageIndex) <
 | 
						|
             mPDFiumEngine->GetPageCount(mPDFDoc));
 | 
						|
 | 
						|
  if (aPageWidth <= 0 || aPageHeight <= 0) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  FPDF_PAGE pdfPage = mPDFiumEngine->LoadPage(mPDFDoc, aPageIndex);
 | 
						|
  NS_ENSURE_TRUE(pdfPage, false);
 | 
						|
 | 
						|
  float scaleFactor = ComputeScaleFactor(::GetDeviceCaps(aDC, HORZRES),
 | 
						|
                                         ::GetDeviceCaps(aDC, VERTRES),
 | 
						|
                                         aPageWidth, aPageHeight);
 | 
						|
  int savedState = ::SaveDC(aDC);
 | 
						|
  ::SetGraphicsMode(aDC, GM_ADVANCED);
 | 
						|
  XFORM xform = { 0 };
 | 
						|
  xform.eM11 = xform.eM22 = scaleFactor;
 | 
						|
  ::ModifyWorldTransform(aDC, &xform, MWT_LEFTMULTIPLY);
 | 
						|
 | 
						|
  // The caller wanted all drawing to happen within the bounds specified.
 | 
						|
  // Based on scale calculations, our destination rect might be larger
 | 
						|
  // than the bounds. Set the clip rect to the bounds.
 | 
						|
  ::IntersectClipRect(aDC, 0, 0, aPageWidth, aPageHeight);
 | 
						|
 | 
						|
  mPDFiumEngine->RenderPage(aDC, pdfPage,
 | 
						|
                            0, 0, aPageWidth, aPageHeight,
 | 
						|
                            0, FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
 | 
						|
  mPDFiumEngine->ClosePage(pdfPage);
 | 
						|
  ::RestoreDC(aDC, savedState);
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PDFViaEMFPrintHelper::DrawPage(HDC aPrinterDC, unsigned int aPageIndex,
 | 
						|
                               int aPageWidth, int aPageHeight)
 | 
						|
{
 | 
						|
  MOZ_ASSERT(aPrinterDC);
 | 
						|
 | 
						|
  // OpenDocument might fail.
 | 
						|
  if (!mPDFDoc) {
 | 
						|
    MOZ_ASSERT_UNREACHABLE("Make sure OpenDocument return true before"
 | 
						|
                           "using DrawPage.");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // There is a comment in Chromium.
 | 
						|
  // https://cs.chromium.org/chromium/src/pdf/pdfium/pdfium_engine.cc?rcl=9ad9f6860b4d6a4ec7f7f975b2c99672e02d5d49&l=4008
 | 
						|
  // Some PDFs seems to render very slowly if RenderPageToDC is directly used
 | 
						|
  // on a printer DC.
 | 
						|
  // The way Chromium works around the issue at the code linked above is to
 | 
						|
  // print to a bitmap and send that to a printer.  Instead of doing that we
 | 
						|
  // render to an EMF file and replay that on the printer DC.  It is unclear
 | 
						|
  // whether our approach will avoid the performance issues though.  Bug
 | 
						|
  // 1359298 covers investigating that.
 | 
						|
 | 
						|
  WindowsEMF emf;
 | 
						|
  bool result = emf.InitForDrawing();
 | 
						|
  NS_ENSURE_TRUE(result, false);
 | 
						|
 | 
						|
  result = RenderPageToDC(emf.GetDC(), aPageIndex, aPageWidth, aPageHeight);
 | 
						|
  NS_ENSURE_TRUE(result, false);
 | 
						|
 | 
						|
  RECT printRect = {0, 0, aPageWidth, aPageHeight};
 | 
						|
  result = emf.Playback(aPrinterDC, printRect);
 | 
						|
  return result;
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PDFViaEMFPrintHelper::DrawPageToFile(const wchar_t* aFilePath,
 | 
						|
                                     unsigned int aPageIndex,
 | 
						|
                                     int aPageWidth, int aPageHeight)
 | 
						|
{
 | 
						|
  // 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();
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
PDFViaEMFPrintHelper::CloseDocument()
 | 
						|
{
 | 
						|
  if (mPDFDoc) {
 | 
						|
    mPDFiumEngine->CloseDocument(mPDFDoc);
 | 
						|
    mPDFDoc = nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
PDFViaEMFPrintHelper::CreatePDFiumEngineIfNeed()
 | 
						|
{
 | 
						|
  if (!mPDFiumEngine) {
 | 
						|
    mPDFiumEngine = PDFiumEngineShim::GetInstanceOrNull();
 | 
						|
  }
 | 
						|
 | 
						|
  return !!mPDFiumEngine;
 | 
						|
} |