PrintTargetSkPDF.cpp (4966B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "PrintTargetSkPDF.h" 7 8 #include "include/core/SkStream.h" 9 #include "mozilla/gfx/2D.h" 10 #include "nsString.h" 11 12 #include "skia/src/pdf/SkPDFUtils.h" 13 14 namespace mozilla::gfx { 15 16 PrintTargetSkPDF::PrintTargetSkPDF(const IntSize& aSize, 17 UniquePtr<SkWStream> aStream) 18 : PrintTarget(/* not using cairo_surface_t */ nullptr, aSize), 19 mOStream(std::move(aStream)), 20 mPageCanvas(nullptr), 21 mRefCanvas(nullptr) {} 22 23 PrintTargetSkPDF::~PrintTargetSkPDF() { 24 Finish(); // ensure stream is flushed 25 26 // Make sure mPDFDoc and mRefPDFDoc are destroyed before our member streams 27 // (which they wrap) are destroyed: 28 mPDFDoc = nullptr; 29 mRefPDFDoc = nullptr; 30 } 31 32 /* static */ 33 already_AddRefed<PrintTargetSkPDF> PrintTargetSkPDF::CreateOrNull( 34 UniquePtr<SkWStream> aStream, const IntSize& aSizeInPoints) { 35 return do_AddRef(new PrintTargetSkPDF(aSizeInPoints, std::move(aStream))); 36 } 37 38 class GkSkWStream final : public SkWStream { 39 public: 40 explicit GkSkWStream(nsIOutputStream* aStream) : mStream(aStream) { 41 MOZ_ASSERT(mStream); 42 } 43 bool write(const void* aBuf, size_t aSize) override { 44 const auto* data = reinterpret_cast<const char*>(aBuf); 45 do { 46 uint32_t wrote = 0; 47 if (NS_WARN_IF(NS_FAILED(mStream->Write(data, aSize, &wrote)))) { 48 return false; 49 } 50 mWritten += wrote; 51 data += wrote; 52 aSize -= wrote; 53 } while (aSize); 54 NS_ASSERTION(aSize == 0, "not everything was written to the file"); 55 return true; 56 } 57 void flush() override { (void)NS_WARN_IF(NS_FAILED(mStream->Flush())); } 58 size_t bytesWritten() const override { return mWritten; } 59 60 private: 61 nsCOMPtr<nsIOutputStream> mStream; 62 size_t mWritten = 0; 63 }; 64 65 already_AddRefed<PrintTargetSkPDF> PrintTargetSkPDF::CreateOrNull( 66 nsIOutputStream* aStream, const IntSize& aSizeInPoints) { 67 return CreateOrNull(MakeUnique<GkSkWStream>(aStream), aSizeInPoints); 68 } 69 70 nsresult PrintTargetSkPDF::BeginPrinting(const nsAString& aTitle, 71 const nsAString& aPrintToFileName, 72 int32_t aStartPage, int32_t aEndPage) { 73 // We need to create the SkPDFDocument here rather than in CreateOrNull 74 // because it's only now that we are given aTitle which we want for the 75 // PDF metadata. 76 77 NS_ConvertUTF16toUTF8 title(aTitle); 78 SkPDF::Metadata metadata; 79 metadata.fTitle = SkString(title.get(), title.Length()); 80 metadata.fCreator = "Firefox"; 81 SkPDF::DateTime now = {0}; 82 SkPDFUtils::GetDateTime(&now); 83 metadata.fCreation = now; 84 metadata.fModified = now; 85 86 // TODO(emilio, bug 2001912): Provide jpeg decoder / encoders for SkPdf? 87 metadata.allowNoJpegs = true; 88 89 // SkDocument stores a non-owning raw pointer to aStream 90 mPDFDoc = SkPDF::MakeDocument(mOStream.get(), metadata); 91 92 return mPDFDoc ? NS_OK : NS_ERROR_FAILURE; 93 } 94 95 nsresult PrintTargetSkPDF::BeginPage(const IntSize& aSizeInPoints) { 96 mPageCanvas = mPDFDoc->beginPage(mSize.width, mSize.height); 97 98 return !mPageCanvas ? NS_ERROR_FAILURE 99 : PrintTarget::BeginPage(aSizeInPoints); 100 } 101 102 nsresult PrintTargetSkPDF::EndPage() { 103 mPageCanvas = nullptr; 104 mPageDT = nullptr; 105 return PrintTarget::EndPage(); 106 } 107 108 nsresult PrintTargetSkPDF::EndPrinting() { 109 mPDFDoc->close(); 110 if (mRefPDFDoc) { 111 mRefPDFDoc->close(); 112 } 113 mPageCanvas = nullptr; 114 mPageDT = nullptr; 115 return NS_OK; 116 } 117 118 void PrintTargetSkPDF::Finish() { 119 if (mIsFinished) { 120 return; 121 } 122 mOStream->flush(); 123 PrintTarget::Finish(); 124 } 125 126 already_AddRefed<DrawTarget> PrintTargetSkPDF::MakeDrawTarget( 127 const IntSize& aSize, DrawEventRecorder* aRecorder) { 128 if (aRecorder) { 129 return PrintTarget::MakeDrawTarget(aSize, aRecorder); 130 } 131 // MOZ_ASSERT(aSize == mSize, "Should mPageCanvas size match?"); 132 if (!mPageCanvas) { 133 return nullptr; 134 } 135 mPageDT = Factory::CreateDrawTargetWithSkCanvas(mPageCanvas); 136 if (!mPageDT) { 137 mPageCanvas = nullptr; 138 return nullptr; 139 } 140 return do_AddRef(mPageDT); 141 } 142 143 already_AddRefed<DrawTarget> PrintTargetSkPDF::GetReferenceDrawTarget() { 144 if (!mRefDT) { 145 SkPDF::Metadata metadata; 146 metadata.allowNoJpegs = true; 147 // SkDocument stores a non-owning raw pointer to aStream 148 mRefPDFDoc = SkPDF::MakeDocument(&mRefOStream, metadata); 149 if (!mRefPDFDoc) { 150 return nullptr; 151 } 152 mRefCanvas = mRefPDFDoc->beginPage(mSize.width, mSize.height); 153 if (!mRefCanvas) { 154 return nullptr; 155 } 156 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetWithSkCanvas(mRefCanvas); 157 if (!dt) { 158 return nullptr; 159 } 160 mRefDT = std::move(dt); 161 } 162 163 return do_AddRef(mRefDT); 164 } 165 166 } // namespace mozilla::gfx