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/. */
6 #include "PrintTargetCG.h"
9 #include "cairo-quartz.h"
10 #include "mozilla/gfx/HelpersCairo.h"
11 #include "nsObjCExceptions.h"
17 PrintTargetCG::PrintTargetCG(PMPrintSession aPrintSession, PMPageFormat aPageFormat,
18 PMPrintSettings aPrintSettings, const IntSize& aSize)
19 : PrintTarget(/* aCairoSurface */ nullptr, aSize),
20 mPrintSession(aPrintSession),
21 mPageFormat(aPageFormat),
22 mPrintSettings(aPrintSettings) {
23 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
25 MOZ_ASSERT(mPrintSession && mPageFormat && mPrintSettings);
27 ::PMRetain(mPrintSession);
28 ::PMRetain(mPageFormat);
29 ::PMRetain(mPrintSettings);
31 // TODO: Add memory reporting like gfxQuartzSurface.
32 // RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
34 NS_OBJC_END_TRY_IGNORE_BLOCK;
37 PrintTargetCG::~PrintTargetCG() {
38 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
40 ::PMRelease(mPrintSession);
41 ::PMRelease(mPageFormat);
42 ::PMRelease(mPrintSettings);
44 NS_OBJC_END_TRY_IGNORE_BLOCK;
47 /* static */ already_AddRefed<PrintTargetCG> PrintTargetCG::CreateOrNull(
48 PMPrintSession aPrintSession, PMPageFormat aPageFormat, PMPrintSettings aPrintSettings,
49 const IntSize& aSize) {
50 if (!Factory::CheckSurfaceSize(aSize)) {
54 RefPtr<PrintTargetCG> target =
55 new PrintTargetCG(aPrintSession, aPageFormat, aPrintSettings, aSize);
57 return target.forget();
60 static size_t PutBytesNull(void* info, const void* buffer, size_t count) { return count; }
62 already_AddRefed<DrawTarget> PrintTargetCG::GetReferenceDrawTarget() {
64 const IntSize size(1, 1);
66 CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
67 CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
68 CGContextRef pdfContext = CGPDFContextCreate(consumer, nullptr, nullptr);
69 CGDataConsumerRelease(consumer);
71 cairo_surface_t* similar =
72 cairo_quartz_surface_create_for_cg_context(pdfContext, size.width, size.height);
74 CGContextRelease(pdfContext);
76 if (cairo_surface_status(similar)) {
80 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(similar, size);
82 // The DT addrefs the surface, so we need drop our own reference to it:
83 cairo_surface_destroy(similar);
85 if (!dt || !dt->IsValid()) {
91 return do_AddRef(mRefDT);
94 nsresult PrintTargetCG::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName,
95 int32_t aStartPage, int32_t aEndPage) {
96 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
98 // Print Core of Application Service sent print job with names exceeding
99 // 255 bytes. This is a workaround until fix it.
100 // (https://openradar.appspot.com/34428043)
101 nsAutoString adjustedTitle;
102 PrintTarget::AdjustPrintJobNameForIPP(aTitle, adjustedTitle);
104 if (!adjustedTitle.IsEmpty()) {
105 CFStringRef cfString = ::CFStringCreateWithCharacters(
106 NULL, reinterpret_cast<const UniChar*>(adjustedTitle.BeginReading()),
107 adjustedTitle.Length());
109 ::PMPrintSettingsSetJobName(mPrintSettings, cfString);
110 ::CFRelease(cfString);
115 status = ::PMSetFirstPage(mPrintSettings, aStartPage, false);
116 NS_ASSERTION(status == noErr, "PMSetFirstPage failed");
117 status = ::PMSetLastPage(mPrintSettings, aEndPage, false);
118 NS_ASSERTION(status == noErr, "PMSetLastPage failed");
120 status = ::PMSessionBeginCGDocumentNoDialog(mPrintSession, mPrintSettings, mPageFormat);
122 return status == noErr ? NS_OK : NS_ERROR_ABORT;
124 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
127 nsresult PrintTargetCG::EndPrinting() {
128 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
130 ::PMSessionEndDocumentNoDialog(mPrintSession);
133 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
136 nsresult PrintTargetCG::AbortPrinting() {
138 mHasActivePage = false;
140 return EndPrinting();
143 nsresult PrintTargetCG::BeginPage() {
144 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
146 PMSessionError(mPrintSession);
147 OSStatus status = ::PMSessionBeginPageNoDialog(mPrintSession, mPageFormat, NULL);
148 if (status != noErr) {
149 return NS_ERROR_ABORT;
152 CGContextRef context;
153 // This call will fail if it wasn't called between the PMSessionBeginPage/
154 // PMSessionEndPage calls:
155 ::PMSessionGetCGGraphicsContext(mPrintSession, &context);
158 return NS_ERROR_FAILURE;
161 unsigned int width = static_cast<unsigned int>(mSize.width);
162 unsigned int height = static_cast<unsigned int>(mSize.height);
164 // Initially, origin is at bottom-left corner of the paper.
165 // Here, we translate it to top-left corner of the paper.
166 CGContextTranslateCTM(context, 0, height);
167 CGContextScaleCTM(context, 1.0, -1.0);
169 cairo_surface_t* surface = cairo_quartz_surface_create_for_cg_context(context, width, height);
171 if (cairo_surface_status(surface)) {
172 return NS_ERROR_FAILURE;
175 mCairoSurface = surface;
177 return PrintTarget::BeginPage();
179 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
182 nsresult PrintTargetCG::EndPage() {
183 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
185 cairo_surface_finish(mCairoSurface);
186 mCairoSurface = nullptr;
188 OSStatus status = ::PMSessionEndPageNoDialog(mPrintSession);
189 if (status != noErr) {
190 return NS_ERROR_ABORT;
193 return PrintTarget::EndPage();
195 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
199 } // namespace mozilla