Bug 1309272, part 4 - Implement a PrintTarget sub-class for printing via Skia PDF. r=lsalzman
☠☠ backed out by bc39d8cde9ab ☠ ☠
authorJonathan Watt <jwatt@jwatt.org>
Fri, 18 Nov 2016 11:30:25 +0000
changeset 324466 125e6fb37319d4767df61d82af7014094163dccb
parent 324465 d5ffc2a28d39c6274a639d002006216c0b668677
child 324467 a9f6271115de9f3cb7e3bea8f4fcbb04d41ace08
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewerslsalzman
bugs1309272
milestone53.0a1
Bug 1309272, part 4 - Implement a PrintTarget sub-class for printing via Skia PDF. r=lsalzman
gfx/thebes/PrintTargetSkPDF.cpp
gfx/thebes/PrintTargetSkPDF.h
gfx/thebes/moz.build
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTargetSkPDF.cpp
@@ -0,0 +1,159 @@
+/* -*- 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
new file mode 100644
--- /dev/null
+++ b/gfx/thebes/PrintTargetSkPDF.h
@@ -0,0 +1,75 @@
+/* -*- 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 */
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -218,16 +218,24 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
         'gfxMacPlatformFontList.mm',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     UNIFIED_SOURCES += [
         'D3D11Checks.cpp',
         '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']:
     USE_LIBS += [
         'icu',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')