1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 "PrintTargetPS.h"
7 
8 #include "cairo.h"
9 #include "cairo-ps.h"
10 
11 namespace mozilla {
12 namespace gfx {
13 
14 static cairo_status_t
write_func(void * closure,const unsigned char * data,unsigned int length)15 write_func(void *closure, const unsigned char *data, unsigned int length)
16 {
17   nsCOMPtr<nsIOutputStream> out = reinterpret_cast<nsIOutputStream*>(closure);
18   do {
19     uint32_t wrote = 0;
20     if (NS_FAILED(out->Write((const char*)data, length, &wrote))) {
21       break;
22     }
23     data += wrote; length -= wrote;
24   } while (length > 0);
25   NS_ASSERTION(length == 0, "not everything was written to the file");
26   return CAIRO_STATUS_SUCCESS;
27 }
28 
PrintTargetPS(cairo_surface_t * aCairoSurface,const IntSize & aSize,nsIOutputStream * aStream,PageOrientation aOrientation)29 PrintTargetPS::PrintTargetPS(cairo_surface_t* aCairoSurface,
30                              const IntSize& aSize,
31                              nsIOutputStream *aStream,
32                              PageOrientation aOrientation)
33   : PrintTarget(aCairoSurface, aSize)
34   , mStream(aStream)
35   , mOrientation(aOrientation)
36 {
37 }
38 
~PrintTargetPS()39 PrintTargetPS::~PrintTargetPS()
40 {
41   // We get called first, then PrintTarget's dtor.  That means that mStream
42   // is destroyed before PrintTarget's dtor calls cairo_surface_destroy.  This
43   // can be a problem if Finish() hasn't been called on us, since
44   // cairo_surface_destroy will then call cairo_surface_finish and that will
45   // end up invoking write_func above with the by now dangling pointer mStream
46   // that mCairoSurface stored.  To prevent that from happening we must call
47   // Flush here before mStream is deleted.
48   Finish();
49 }
50 
51 /* static */ already_AddRefed<PrintTargetPS>
CreateOrNull(nsIOutputStream * aStream,IntSize aSizeInPoints,PageOrientation aOrientation)52 PrintTargetPS::CreateOrNull(nsIOutputStream *aStream,
53                             IntSize aSizeInPoints,
54                             PageOrientation aOrientation)
55 {
56   // The PS output does not specify the page size so to print landscape we need
57   // to rotate the drawing 90 degrees and print on portrait paper.  If printing
58   // landscape, swap the width/height supplied to cairo to select a portrait
59   // print area.  Our consumers are responsible for checking
60   // RotateForLandscape() and applying a rotation transform if true.
61   if (aOrientation == LANDSCAPE) {
62     Swap(aSizeInPoints.width, aSizeInPoints.height);
63   }
64 
65   cairo_surface_t* surface =
66     cairo_ps_surface_create_for_stream(write_func, (void*)aStream,
67                                        aSizeInPoints.width,
68                                        aSizeInPoints.height);
69   if (cairo_surface_status(surface)) {
70     return nullptr;
71   }
72   cairo_ps_surface_restrict_to_level(surface, CAIRO_PS_LEVEL_2);
73 
74   // The new object takes ownership of our surface reference.
75   RefPtr<PrintTargetPS> target = new PrintTargetPS(surface, aSizeInPoints,
76                                                    aStream, aOrientation);
77   return target.forget();
78 }
79 
80 nsresult
BeginPrinting(const nsAString & aTitle,const nsAString & aPrintToFileName)81 PrintTargetPS::BeginPrinting(const nsAString& aTitle,
82                              const nsAString& aPrintToFileName)
83 {
84   if (mOrientation == PORTRAIT) {
85     cairo_ps_surface_dsc_comment(mCairoSurface, "%%Orientation: Portrait");
86   } else {
87     cairo_ps_surface_dsc_comment(mCairoSurface, "%%Orientation: Landscape");
88   }
89   return NS_OK;
90 }
91 
92 nsresult
EndPage()93 PrintTargetPS::EndPage()
94 {
95   cairo_surface_show_page(mCairoSurface);
96   return NS_OK;
97 }
98 
99 void
Finish()100 PrintTargetPS::Finish()
101 {
102   if (mIsFinished) {
103     return; // We don't want to call Close() on mStream more than once
104   }
105   PrintTarget::Finish();
106   mStream->Close();
107 }
108 
109 } // namespace gfx
110 } // namespace mozilla
111