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