gecko-dev/gfx/thebes/PrintTargetEMF.cpp

208 строки
5.9 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 "PrintTargetEMF.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsIFile.h"
#include "nsNativeCharsetUtils.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)
, mChannelBroken(false)
{
}
PrintTargetEMF::~PrintTargetEMF()
{
if (mPDFiumProcess) {
mPDFiumProcess->Delete();
}
}
/* static */ already_AddRefed<PrintTargetEMF>
PrintTargetEMF::CreateOrNull(HDC aDC, const IntSize& aSizeInPoints)
{
return do_AddRef(new PrintTargetEMF(aDC, aSizeInPoints));
}
nsresult
PrintTargetEMF::BeginPrinting(const nsAString& aTitle,
const nsAString& aPrintToFileName,
int32_t aStartPage,
int32_t aEndPage)
{
mTitle = aTitle;
const uint32_t DOC_TITLE_LENGTH = MAX_PATH - 1;
DOCINFOW docinfo;
nsString titleStr(aTitle);
if (titleStr.Length() > DOC_TITLE_LENGTH) {
titleStr.SetLength(DOC_TITLE_LENGTH - 3);
titleStr.AppendLiteral("...");
}
nsString docName(aPrintToFileName);
docinfo.cbSize = sizeof(docinfo);
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(this), NS_ERROR_FAILURE);
mChannelBroken = false;
return NS_OK;
}
nsresult
PrintTargetEMF::EndPrinting()
{
mPDFiumProcess->GetActor()->EndConversion();
return (::EndDoc(mPrinterDC) <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetEMF::AbortPrinting()
{
return (::AbortDoc(mPrinterDC) <= 0) ? NS_ERROR_FAILURE : NS_OK;
}
nsresult
PrintTargetEMF::BeginPage()
{
MOZ_ASSERT(!mPDFFileForOnePage && !mTargetForCurrentPage);
NS_ENSURE_TRUE(!mChannelBroken, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(::StartPage(mPrinterDC) >0, NS_ERROR_FAILURE);
// We create a new file for each page so that we can make sure each new
// mPDFFileForOnePage contains one single page.
nsresult rv =
NS_OpenAnonymousTemporaryNsIFile(getter_AddRefs(mPDFFileForOnePage));
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
nsAutoCString filePath;
#ifdef XP_WIN
// Unfortunately, SkFILEWStream does not support wide char paths yet.
// We have to use the native charset even though it is lossy :(
nsAutoString filePathU;
mPDFFileForOnePage->GetPath(filePathU);
NS_CopyUnicodeToNative(filePathU, filePath);
#else
mPDFFileForOnePage->GetNativePath(filePath);
#endif
auto stream = MakeUnique<SkFILEWStream>(filePath.get());
// Creating a new PrintTargetSkPDF for each page so that we can convert each
// of them into EMF contents individually by the PDFium processes.
mTargetForCurrentPage = PrintTargetSkPDF::CreateOrNull(std::move(stream), mSize);
mTargetForCurrentPage->BeginPrinting(mTitle, NS_LITERAL_STRING(""), 0, 0);
mTargetForCurrentPage->BeginPage();
return NS_OK;
}
nsresult
PrintTargetEMF::EndPage()
{
NS_ENSURE_TRUE(!mChannelBroken, NS_ERROR_FAILURE);
mTargetForCurrentPage->EndPage();
mTargetForCurrentPage->EndPrinting();
mTargetForCurrentPage->Finish();
mTargetForCurrentPage = nullptr;
PRFileDesc* prfile;
nsresult rv = mPDFFileForOnePage->OpenNSPRFileDesc(PR_RDONLY, PR_IRWXU,
&prfile);
NS_ENSURE_SUCCESS(rv, rv);
FileDescriptor descriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfile)));
if (!mPDFiumProcess->GetActor()->SendConvertToEMF(descriptor,
::GetDeviceCaps(mPrinterDC, HORZRES),
::GetDeviceCaps(mPrinterDC, VERTRES)))
{
return NS_ERROR_FAILURE;
}
PR_Close(prfile);
return NS_OK;
}
already_AddRefed<DrawTarget>
PrintTargetEMF::MakeDrawTarget(const IntSize& aSize,
DrawEventRecorder* aRecorder)
{
MOZ_ASSERT(!mChannelBroken);
return mTargetForCurrentPage->MakeDrawTarget(aSize, aRecorder);
}
already_AddRefed<DrawTarget>
PrintTargetEMF::GetReferenceDrawTarget()
{
if (!mRefTarget) {
auto dummy = MakeUnique<SkNullWStream>();
mRefTarget = PrintTargetSkPDF::CreateOrNull(std::move(dummy), mSize);
}
if (!mRefDT) {
mRefDT = mRefTarget->GetReferenceDrawTarget();
}
return mRefDT.forget();
}
void
PrintTargetEMF::ConvertToEMFDone(const nsresult& aResult,
mozilla::ipc::Shmem&& aEMF)
{
MOZ_ASSERT_IF(NS_FAILED(aResult), aEMF.Size<uint8_t>() == 0);
MOZ_ASSERT(!mChannelBroken, "It is not possible to get conversion callback "
"after the channel was broken.");
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;
if (mPageDoneCallback) {
mPageDoneCallback(aResult);
}
}
} // namespace gfx
} // namespace mozilla