Commit 85498796 authored by Jonathan Watt's avatar Jonathan Watt
Browse files

Bug 1309272, part 4 - Implement a PrintTarget sub-class for printing via Skia PDF. r=lsalzman

parent 6c16f3cb
Loading
Loading
Loading
Loading
+159 −0
Original line number Diff line number Diff line
/* -*- 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 "PrintTargetSkPDF.h"

#include "mozilla/gfx/2D.h"
#include "nsString.h"
#include <vector>

namespace mozilla {
namespace gfx {

PrintTargetSkPDF::PrintTargetSkPDF(const IntSize& aSize,
                                   UniquePtr<SkWStream> aStream)
  : PrintTarget(/* not using cairo_surface_t */ nullptr, aSize)
  , mOStream(Move(aStream))
  , mPageCanvas(nullptr)
{
}

PrintTargetSkPDF::~PrintTargetSkPDF()
{
  Finish(); // ensure stream is flushed

  // Make sure mPDFDoc and mRefPDFDoc are destroyed before our member streams
  // (which they wrap) are destroyed:
  mPDFDoc = nullptr;
  mRefPDFDoc = nullptr;
}

/* static */ already_AddRefed<PrintTargetSkPDF>
PrintTargetSkPDF::CreateOrNull(UniquePtr<SkWStream> aStream,
                               const IntSize& aSizeInPoints)
{
  return do_AddRef(new PrintTargetSkPDF(aSizeInPoints, Move(aStream)));
}

nsresult
PrintTargetSkPDF::BeginPrinting(const nsAString& aTitle,
                                const nsAString& aPrintToFileName,
                                int32_t aStartPage,
                                int32_t aEndPage)
{
  // We need to create the SkPDFDocument here rather than in CreateOrNull
  // because it's only now that we are given aTitle which we want for the
  // PDF metadata.

  SkDocument::PDFMetadata metadata;
  metadata.fTitle = NS_ConvertUTF16toUTF8(aTitle).get();
  metadata.fCreator = "Firefox";
  SkTime::DateTime now;
  SkTime::GetDateTime(&now);
  metadata.fCreation.fEnabled = true;
  metadata.fCreation.fDateTime = now;
  metadata.fModified.fEnabled = true;
  metadata.fModified.fDateTime = now;

  // SkDocument stores a non-owning raw pointer to aStream
  mPDFDoc = SkDocument::MakePDF(mOStream.get(), SK_ScalarDefaultRasterDPI,
                                metadata, /*jpegEncoder*/ nullptr, true);

  return mPDFDoc ? NS_OK : NS_ERROR_FAILURE;
}

nsresult
PrintTargetSkPDF::BeginPage()
{
  mPageCanvas = sk_ref_sp(mPDFDoc->beginPage(mSize.width, mSize.height));

  return !mPageCanvas ? NS_ERROR_FAILURE : PrintTarget::BeginPage();
}

nsresult
PrintTargetSkPDF::EndPage()
{
  mPageCanvas = nullptr;
  mPageDT = nullptr;
  return PrintTarget::EndPage();
}

nsresult
PrintTargetSkPDF::EndPrinting()
{
  mPDFDoc->close();
  if (mRefPDFDoc) {
    mRefPDFDoc->close();
  }
  mPageCanvas = nullptr;
  mPageDT = nullptr;
  return NS_OK;
}

void
PrintTargetSkPDF::Finish()
{
  if (mIsFinished) {
    return;
  }
  mOStream->flush();
  PrintTarget::Finish();
}

already_AddRefed<DrawTarget>
PrintTargetSkPDF::MakeDrawTarget(const IntSize& aSize,
                                 DrawEventRecorder* aRecorder)
{
  if (aRecorder) {
    return PrintTarget::MakeDrawTarget(aSize, aRecorder);
  }
  //MOZ_ASSERT(aSize == mSize, "Should mPageCanvas size match?");
  if (!mPageCanvas) {
    return nullptr;
  }
  mPageDT = Factory::CreateDrawTargetWithSkCanvas(mPageCanvas.get());
  if (!mPageDT) {
    mPageCanvas = nullptr;
    return nullptr;
  }
  return do_AddRef(mPageDT);
}

already_AddRefed<DrawTarget>
PrintTargetSkPDF::GetReferenceDrawTarget(DrawEventRecorder* aRecorder)
{
  if (!mRefDT) {
    SkDocument::PDFMetadata metadata;
    // SkDocument stores a non-owning raw pointer to aStream
    mRefPDFDoc = SkDocument::MakePDF(&mRefOStream,
                                     SK_ScalarDefaultRasterDPI,
                                     metadata, nullptr, true);
    if (!mRefPDFDoc) {
      return nullptr;
    }
    mRefCanvas = sk_ref_sp(mRefPDFDoc->beginPage(mSize.width, mSize.height));
    if (!mRefCanvas) {
      return nullptr;
    }
    RefPtr<DrawTarget> dt =
      Factory::CreateDrawTargetWithSkCanvas(mRefCanvas.get());
    if (!dt) {
      return nullptr;
    }

    if (aRecorder) {
      dt = CreateRecordingDrawTarget(aRecorder, dt);
      if (!dt || !dt->IsValid()) {
        return nullptr;
      }
    }

    mRefDT = dt.forget();
  }
  return do_AddRef(mRefDT);
}

} // namespace gfx
} // namespace mozilla
+75 −0
Original line number Diff line number Diff line
/* -*- 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/. */

#ifndef MOZILLA_GFX_PRINTTARGETSKPDF_H
#define MOZILLA_GFX_PRINTTARGETSKPDF_H

#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "PrintTarget.h"
#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkStream.h"

namespace mozilla {
namespace gfx {

/**
 * Skia PDF printing target.
 */
class PrintTargetSkPDF final : public PrintTarget
{
public:
  // The returned PrintTargetSkPDF keeps a raw pointer to the passed SkWStream
  // but does not own it.  Callers are responsible for ensuring that passed
  // stream outlives the returned PrintTarget.
  static already_AddRefed<PrintTargetSkPDF>
  CreateOrNull(UniquePtr<SkWStream> aStream,
               const IntSize& aSizeInPoints);

  virtual nsresult BeginPrinting(const nsAString& aTitle,
                                 const nsAString& aPrintToFileName,
                                 int32_t aStartPage,
                                 int32_t aEndPage) override;
  virtual nsresult EndPrinting() override;
  virtual void Finish() override;

  virtual nsresult BeginPage() override;
  virtual nsresult EndPage() override;

  virtual already_AddRefed<DrawTarget>
  MakeDrawTarget(const IntSize& aSize,
                 DrawEventRecorder* aRecorder = nullptr) final;

  virtual already_AddRefed<DrawTarget>
  GetReferenceDrawTarget(DrawEventRecorder* aRecorder) override final;

private:
  PrintTargetSkPDF(const IntSize& aSize,
                   UniquePtr<SkWStream> aStream);
  virtual ~PrintTargetSkPDF();

  // Do not hand out references to this object.  It holds a non-owning
  // reference to mOStreame, so must not outlive mOStream.
  sk_sp<SkDocument> mPDFDoc;

  // The stream that the SkDocument outputs to.
  UniquePtr<SkWStream> mOStream;

  // The current page's SkCanvas and its wrapping DrawTarget:
  sk_sp<SkCanvas> mPageCanvas;
  RefPtr<DrawTarget> mPageDT;

  // Members needed to provide a reference DrawTarget:
  sk_sp<SkDocument> mRefPDFDoc;
  sk_sp<SkCanvas> mRefCanvas;
  SkDynamicMemoryWStream mRefOStream;
  RefPtr<DrawTarget> mRefDT;
};

} // namespace gfx
} // namespace mozilla

#endif /* MOZILLA_GFX_PRINTTARGETSKPDF_H */
+8 −0
Original line number Diff line number Diff line
@@ -223,6 +223,14 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
        'DeviceManagerDx.cpp',
    ]

if CONFIG['MOZ_ENABLE_SKIA_PDF']:
    EXPORTS.mozilla.gfx += [
        'PrintTargetSkPDF.h',
    ]
    SOURCES += [
        'PrintTargetSkPDF.cpp',
    ]

# We prefer to use ICU for normalization functions, but currently it is only
# available if we're building with the Intl API enabled:
if CONFIG['ENABLE_INTL_API']: