Bug 1370488 - Add support for having printing on Windows print via Skia PDF and PDFium r=jwatt

1. Convert PDF to EMF via PDFViaEMFPrintHelper.
2. Replay EMF on printer DC.

MozReview-Commit-ID: 8YTcaZ2Y1rO

--HG--
extra : rebase_source : 7b5e718c7fe5dbeee13bbd7a0c958cd285d0833f
This commit is contained in:
Farmer Tseng 2017-06-08 18:55:42 +08:00
Родитель ee4e319c47
Коммит d8467854d7
2 изменённых файлов: 222 добавлений и 4 удалений

Просмотреть файл

@ -37,6 +37,7 @@
#ifdef MOZ_ENABLE_SKIA_PDF
#include "mozilla/gfx/PrintTargetSkPDF.h"
#include "nsIUUIDGenerator.h"
#include "mozilla/widget/PDFViaEMFPrintHelper.h"
#endif
static mozilla::LazyLogModule kWidgetPrintingLogMod("printing-widget");
@ -45,6 +46,10 @@ static mozilla::LazyLogModule kWidgetPrintingLogMod("printing-widget");
using namespace mozilla;
using namespace mozilla::gfx;
#ifdef MOZ_ENABLE_SKIA_PDF
using namespace mozilla::widget;
#endif
static const wchar_t kDriverName[] = L"WINSPOOL";
//----------------------------------------------------------------------------------
@ -93,7 +98,11 @@ nsDeviceContextSpecWin::nsDeviceContextSpecWin()
mDeviceName = nullptr;
mDevMode = nullptr;
#ifdef MOZ_ENABLE_SKIA_PDF
mPrintViaSkPDF = false;
mPrintViaSkPDF = false;
mDC = NULL;
mPDFPageCount = 0;
mPDFCurrentPageNum = 0;
mPrintViaPDFInProgress = false;
#endif
}
@ -115,6 +124,11 @@ nsDeviceContextSpecWin::~nsDeviceContextSpecWin()
psWin->SetDevMode(nullptr);
}
#ifdef MOZ_ENABLE_SKIA_PDF
if (mPrintViaSkPDF ) {
CleanupPrintViaPDF();
}
#endif
// Free them, we won't need them for a while
GlobalPrinters::GetInstance()->FreeGlobalPrinters();
}
@ -259,6 +273,40 @@ already_AddRefed<PrintTarget> nsDeviceContextSpecWin::MakePrintTarget()
auto skStream = MakeUnique<SkFILEWStream>(printFile.get());
return PrintTargetSkPDF::CreateOrNull(Move(skStream), size);
}
if (mDevMode) {
// When printing to a printer via Skia PDF we open a temporary file that
// we draw the print output into as PDF output, then once we reach
// EndDcoument we'll convert that PDF file to EMF page by page to print
// each page. Here we create the temporary file and wrap it in a
// PrintTargetSkPDF that we return.
nsresult rv =
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mPDFTempFile));
NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
nsID uuid;
rv = uuidGenerator->GenerateUUIDInPlace(&uuid);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
char uuidChars[NSID_LENGTH];
uuid.ToProvidedString(uuidChars);
nsAutoCString printFile("tmp-printing");
printFile.Append(nsPrintfCString("%s.pdf", uuidChars));
rv = mPDFTempFile->AppendNative(printFile);
NS_ENSURE_SUCCESS(rv, nullptr);
nsAutoCString filePath;
mPDFTempFile->GetNativePath(filePath);
auto skStream = MakeUnique<SkFILEWStream>(filePath.get());
return PrintTargetSkPDF::CreateOrNull(Move(skStream), size);
}
}
#endif
@ -312,6 +360,11 @@ nsDeviceContextSpecWin::GetDPI()
{
// To match the previous printing code we need to return 72 when printing to
// PDF and 144 when printing to a Windows surface.
#ifdef MOZ_ENABLE_SKIA_PDF
if (mPrintViaSkPDF) {
return 72.0f;
}
#endif
return mOutputFormat == nsIPrintSettings::kOutputFormatPDF ? 72.0f : 144.0f;
}
@ -319,7 +372,11 @@ float
nsDeviceContextSpecWin::GetPrintingScale()
{
MOZ_ASSERT(mPrintSettings);
#ifdef MOZ_ENABLE_SKIA_PDF
if (mPrintViaSkPDF) {
return 1.0f; // PDF is vector based, so we don't need a scale
}
#endif
// To match the previous printing code there is no scaling for PDF.
if (mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
return 1.0f;
@ -331,6 +388,145 @@ nsDeviceContextSpecWin::GetPrintingScale()
return float(resolution) / GetDPI();
}
#ifdef MOZ_ENABLE_SKIA_PDF
void
nsDeviceContextSpecWin::CleanupPrintViaPDF()
{
if (mPDFPrintHelper) {
mPDFPrintHelper->CloseDocument();
mPDFPrintHelper = nullptr;
mPDFPageCount = 0;
}
if (mPDFTempFile) {
mPDFTempFile->Remove(/* aRecursive */ false);
mPDFTempFile = nullptr;
}
if (mDC != NULL) {
if (mPrintViaPDFInProgress) {
::EndDoc(mDC);
mPrintViaPDFInProgress = false;
}
::DeleteDC(mDC);
mDC = NULL;
}
}
void
nsDeviceContextSpecWin::FinishPrintViaPDF()
{
MOZ_ASSERT(mDC != NULL);
MOZ_ASSERT(mPDFPrintHelper);
MOZ_ASSERT(mPDFTempFile);
MOZ_ASSERT(mPrintViaPDFInProgress);
bool isPrinted = false;
bool endPageSuccess = false;
if (::StartPage(mDC) > 0) {
isPrinted = mPDFPrintHelper->DrawPage(mDC, mPDFCurrentPageNum++,
::GetDeviceCaps(mDC, HORZRES),
::GetDeviceCaps(mDC, VERTRES));
if (::EndPage(mDC) > 0) {
endPageSuccess = true;
}
}
if (mPDFCurrentPageNum < mPDFPageCount && isPrinted && endPageSuccess) {
nsresult rv = NS_DispatchToCurrentThread(NewRunnableMethod(
"nsDeviceContextSpecWin::PrintPDFOnThread",
this,
&nsDeviceContextSpecWin::FinishPrintViaPDF));
if (NS_SUCCEEDED(rv)) {
return;
}
}
CleanupPrintViaPDF();
}
#endif
nsresult
nsDeviceContextSpecWin::BeginDocument(const nsAString& aTitle,
const nsAString& aPrintToFileName,
int32_t aStartPage,
int32_t aEndPage)
{
#ifdef MOZ_ENABLE_SKIA_PDF
if (mPrintViaSkPDF && (mOutputFormat != nsIPrintSettings::kOutputFormatPDF)) {
// Here we create mDC which we'll draw each page from our temporary PDF file
// to once we reach EndDocument. The only reason we create it here rather
// than in EndDocument is so that we don't need to store aTitle and
// aPrintToFileName as member data.
NS_WARNING_ASSERTION(mDriverName, "No driver!");
mDC = ::CreateDCW(mDriverName, mDeviceName, nullptr, mDevMode);
if (mDC == NULL) {
gfxCriticalError(gfxCriticalError::DefaultOptions(false))
<< "Failed to create device context in GetSurfaceForPrinter";
return NS_ERROR_FAILURE;
}
const uint32_t DOC_TITLE_LENGTH = MAX_PATH - 1;
nsString title(aTitle);
nsString printToFileName(aPrintToFileName);
if (title.Length() > DOC_TITLE_LENGTH) {
title.SetLength(DOC_TITLE_LENGTH - 3);
title.AppendLiteral("...");
}
DOCINFOW di;
di.cbSize = sizeof(di);
di.lpszDocName = title.Length() > 0 ? title.get() : L"Mozilla Document";
di.lpszOutput = printToFileName.Length() > 0 ?
printToFileName.get() : nullptr;
di.lpszDatatype = nullptr;
di.fwType = 0;
if (::StartDocW(mDC, &di) <= 0) {
// Defer calling CleanupPrintViaPDF() in destructor because PDF temp file
// is not ready yet.
return NS_ERROR_FAILURE;
}
mPrintViaPDFInProgress = true;
}
#endif
return NS_OK;
}
nsresult
nsDeviceContextSpecWin::EndDocument()
{
nsresult rv = NS_OK;
#ifdef MOZ_ENABLE_SKIA_PDF
if (mPrintViaSkPDF &&
mOutputFormat != nsIPrintSettings::kOutputFormatPDF &&
mPrintViaPDFInProgress) {
mPDFPrintHelper = MakeUnique<PDFViaEMFPrintHelper>();
rv = mPDFPrintHelper->OpenDocument(mPDFTempFile);
NS_ENSURE_SUCCESS(rv, rv);
mPDFPageCount = mPDFPrintHelper->GetPageCount();
if (mPDFPageCount <= 0) {
CleanupPrintViaPDF();
return NS_ERROR_FAILURE;
}
mPDFCurrentPageNum = 0;
rv = NS_DispatchToCurrentThread(NewRunnableMethod(
"nsDeviceContextSpecWin::PrintPDFOnThread",
this,
&nsDeviceContextSpecWin::FinishPrintViaPDF));
if (NS_FAILED(rv)) {
CleanupPrintViaPDF();
NS_WARNING("Failed to dispatch to the current thread!");
}
}
#endif
return rv;
}
//----------------------------------------------------------------------------------
void nsDeviceContextSpecWin::SetDeviceName(char16ptr_t aDeviceName)
{

Просмотреть файл

@ -17,8 +17,18 @@
class nsIWidget;
#ifdef MOZ_ENABLE_SKIA_PDF
namespace mozilla {
namespace widget {
class PDFViaEMFPrintHelper;
}
}
#endif
class nsDeviceContextSpecWin : public nsIDeviceContextSpec
{
typedef mozilla::widget::PDFViaEMFPrintHelper PDFViaEMFPrintHelper;
public:
nsDeviceContextSpecWin();
@ -28,8 +38,8 @@ public:
NS_IMETHOD BeginDocument(const nsAString& aTitle,
const nsAString& aPrintToFileName,
int32_t aStartPage,
int32_t aEndPage) override { return NS_OK; }
NS_IMETHOD EndDocument() override { return NS_OK; }
int32_t aEndPage) override;
NS_IMETHOD EndDocument() override;
NS_IMETHOD BeginPage() override { return NS_OK; }
NS_IMETHOD EndPage() override { return NS_OK; }
@ -67,7 +77,19 @@ protected:
int16_t mOutputFormat = nsIPrintSettings::kOutputFormatNative;
#ifdef MOZ_ENABLE_SKIA_PDF
void FinishPrintViaPDF();
void CleanupPrintViaPDF();
// This variable is independant of nsIPrintSettings::kOutputFormatPDF.
// It controls both whether normal printing is done via PDF using Skia and
// whether print-to-PDF uses Skia.
bool mPrintViaSkPDF;
nsCOMPtr<nsIFile> mPDFTempFile;
HDC mDC;
bool mPrintViaPDFInProgress;
mozilla::UniquePtr<PDFViaEMFPrintHelper> mPDFPrintHelper;
int mPDFPageCount;
int mPDFCurrentPageNum;
#endif
};