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 
8 #include "cairo.h"
9 #include "cairo-pdf.h"
10 #include "mozilla/AppShutdown.h"
11 
12 namespace mozilla::gfx {
13 
write_func(void * closure,const unsigned char * data,unsigned int length)14 static cairo_status_t write_func(void* closure, const unsigned char* data,
15                                  unsigned int length) {
16   if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
17     return CAIRO_STATUS_SUCCESS;
18   }
19   nsCOMPtr<nsIOutputStream> out = reinterpret_cast<nsIOutputStream*>(closure);
20   do {
21     uint32_t wrote = 0;
22     if (NS_FAILED(out->Write((const char*)data, length, &wrote))) {
23       break;
24     }
25     data += wrote;
26     length -= wrote;
27   } while (length > 0);
28   NS_ASSERTION(length == 0, "not everything was written to the file");
29   return CAIRO_STATUS_SUCCESS;
30 }
31 
PrintTargetPDF(cairo_surface_t * aCairoSurface,const IntSize & aSize,nsIOutputStream * aStream)32 PrintTargetPDF::PrintTargetPDF(cairo_surface_t* aCairoSurface,
33                                const IntSize& aSize, nsIOutputStream* aStream)
34     : PrintTarget(aCairoSurface, aSize), mStream(aStream) {}
35 
~PrintTargetPDF()36 PrintTargetPDF::~PrintTargetPDF() {
37   // We get called first, then PrintTarget's dtor.  That means that mStream
38   // is destroyed before PrintTarget's dtor calls cairo_surface_destroy.  This
39   // can be a problem if Finish() hasn't been called on us, since
40   // cairo_surface_destroy will then call cairo_surface_finish and that will
41   // end up invoking write_func above with the by now dangling pointer mStream
42   // that mCairoSurface stored.  To prevent that from happening we must call
43   // Flush here before mStream is deleted.
44   Finish();
45 }
46 
47 /* static */
CreateOrNull(nsIOutputStream * aStream,const IntSize & aSizeInPoints)48 already_AddRefed<PrintTargetPDF> PrintTargetPDF::CreateOrNull(
49     nsIOutputStream* aStream, const IntSize& aSizeInPoints) {
50   cairo_surface_t* surface = cairo_pdf_surface_create_for_stream(
51       write_func, (void*)aStream, aSizeInPoints.width, aSizeInPoints.height);
52   if (cairo_surface_status(surface)) {
53     return nullptr;
54   }
55 
56   // The new object takes ownership of our surface reference.
57   RefPtr<PrintTargetPDF> target =
58       new PrintTargetPDF(surface, aSizeInPoints, aStream);
59   return target.forget();
60 }
61 
EndPage()62 nsresult PrintTargetPDF::EndPage() {
63   cairo_surface_show_page(mCairoSurface);
64   return PrintTarget::EndPage();
65 }
66 
Finish()67 void PrintTargetPDF::Finish() {
68   if (mIsFinished ||
69       AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
70     // We don't want to call Close() on mStream more than once, and we don't
71     // want to block shutdown if for some reason the user shuts down the
72     // browser mid print.
73     return;
74   }
75   PrintTarget::Finish();
76   mStream->Close();
77 }
78 
79 }  // namespace mozilla::gfx
80