PrintTargetPDF.cpp (4143B)
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 "PrintTargetPDF.h" 7 #ifdef MOZ_ENABLE_SKIA_PDF 8 # include "PrintTargetSkPDF.h" 9 #endif 10 11 #include "cairo.h" 12 #include "cairo-pdf.h" 13 #include "mozilla/AppShutdown.h" 14 #include "mozilla/StaticPrefs_print.h" 15 #include "nsContentUtils.h" 16 #include "nsString.h" 17 18 namespace mozilla::gfx { 19 20 static cairo_status_t write_func(void* closure, const unsigned char* data, 21 unsigned int length) { 22 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { 23 return CAIRO_STATUS_SUCCESS; 24 } 25 nsCOMPtr<nsIOutputStream> out = reinterpret_cast<nsIOutputStream*>(closure); 26 do { 27 uint32_t wrote = 0; 28 if (NS_FAILED(out->Write((const char*)data, length, &wrote))) { 29 break; 30 } 31 data += wrote; 32 length -= wrote; 33 } while (length > 0); 34 NS_ASSERTION(length == 0, "not everything was written to the file"); 35 return CAIRO_STATUS_SUCCESS; 36 } 37 38 PrintTargetPDF::PrintTargetPDF(cairo_surface_t* aCairoSurface, 39 const IntSize& aSize, nsIOutputStream* aStream) 40 : PrintTarget(aCairoSurface, aSize), mStream(aStream) {} 41 42 PrintTargetPDF::~PrintTargetPDF() { 43 // We get called first, then PrintTarget's dtor. That means that mStream 44 // is destroyed before PrintTarget's dtor calls cairo_surface_destroy. This 45 // can be a problem if Finish() hasn't been called on us, since 46 // cairo_surface_destroy will then call cairo_surface_finish and that will 47 // end up invoking write_func above with the by now dangling pointer mStream 48 // that mCairoSurface stored. To prevent that from happening we must call 49 // Flush here before mStream is deleted. 50 Finish(); 51 } 52 53 /* static */ 54 already_AddRefed<PrintTarget> PrintTargetPDF::CreateOrNull( 55 nsIOutputStream* aStream, const IntSize& aSizeInPoints) { 56 if (NS_WARN_IF(!aStream)) { 57 return nullptr; 58 } 59 60 #ifdef MOZ_ENABLE_SKIA_PDF 61 if (StaticPrefs::print_experimental_skpdf()) { 62 return PrintTargetSkPDF::CreateOrNull(aStream, aSizeInPoints); 63 } 64 #endif 65 66 cairo_surface_t* surface = cairo_pdf_surface_create_for_stream( 67 write_func, (void*)aStream, aSizeInPoints.width, aSizeInPoints.height); 68 if (cairo_surface_status(surface)) { 69 return nullptr; 70 } 71 72 nsAutoString creatorName; 73 if (NS_SUCCEEDED(nsContentUtils::GetLocalizedString( 74 nsContentUtils::eBRAND_PROPERTIES, "brandFullName", creatorName)) && 75 !creatorName.IsEmpty()) { 76 creatorName.Append(u" " MOZILLA_VERSION); 77 cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_CREATOR, 78 NS_ConvertUTF16toUTF8(creatorName).get()); 79 } 80 81 // The new object takes ownership of our surface reference. 82 RefPtr<PrintTargetPDF> target = 83 new PrintTargetPDF(surface, aSizeInPoints, aStream); 84 return target.forget(); 85 } 86 87 nsresult PrintTargetPDF::BeginPage(const IntSize& aSizeInPoints) { 88 if (StaticPrefs:: 89 print_save_as_pdf_use_page_rule_size_as_paper_size_enabled()) { 90 cairo_pdf_surface_set_size(mCairoSurface, aSizeInPoints.width, 91 aSizeInPoints.height); 92 if (cairo_surface_status(mCairoSurface)) { 93 return NS_ERROR_FAILURE; 94 } 95 } 96 MOZ_ALWAYS_SUCCEEDS(PrintTarget::BeginPage(aSizeInPoints)); 97 return NS_OK; 98 } 99 100 nsresult PrintTargetPDF::EndPage() { 101 cairo_surface_show_page(mCairoSurface); 102 bool cairoFailure = cairo_surface_status(mCairoSurface); 103 MOZ_ALWAYS_SUCCEEDS(PrintTarget::EndPage()); 104 return cairoFailure ? NS_ERROR_FAILURE : NS_OK; 105 } 106 107 void PrintTargetPDF::Finish() { 108 if (mIsFinished || 109 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { 110 // We don't want to call Close() on mStream more than once, and we don't 111 // want to block shutdown if for some reason the user shuts down the 112 // browser mid print. 113 return; 114 } 115 PrintTarget::Finish(); 116 mStream->Close(); 117 } 118 119 } // namespace mozilla::gfx