1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "printing/printing_context_mac.h"
7 #import <ApplicationServices/ApplicationServices.h>
8 #import <AppKit/AppKit.h>
10 #include "base/logging.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/mac/scoped_nsautorelease_pool.h"
13 #include "base/mac/scoped_nsexception_enabler.h"
14 #include "base/sys_string_conversions.h"
15 #include "base/values.h"
16 #include "printing/print_settings_initializer_mac.h"
18 static const CFStringRef kColorModel = CFSTR("ColorModel");
19 static const CFStringRef kGrayColor = CFSTR("Gray");
24 PrintingContext* PrintingContext::Create(const std::string& app_locale) {
25 return static_cast<PrintingContext*>(new PrintingContextMac(app_locale));
28 PrintingContextMac::PrintingContextMac(const std::string& app_locale)
29 : PrintingContext(app_locale),
30 print_info_([[NSPrintInfo sharedPrintInfo] copy]),
34 PrintingContextMac::~PrintingContextMac() {
38 void PrintingContextMac::AskUserForSettings(gfx::NativeView parent_view,
41 PrintSettingsCallback* callback) {
42 // Third-party print drivers seem to be an area prone to raising exceptions.
43 // This will allow exceptions to be raised, but does not handle them. The
44 // NSPrintPanel appears to have appropriate NSException handlers.
45 base::mac::ScopedNSExceptionEnabler enabler;
47 // Exceptions can also happen when the NSPrintPanel is being
48 // deallocated, so it must be autoreleased within this scope.
49 base::mac::ScopedNSAutoreleasePool pool;
51 DCHECK([NSThread isMainThread]);
53 // We deliberately don't feed max_pages into the dialog, because setting
54 // NSPrintLastPage makes the print dialog pre-select the option to only print
57 // TODO(stuartmorgan): implement 'print selection only' (probably requires
58 // adding a new custom view to the panel on 10.5; 10.6 has
59 // NSPrintPanelShowsPrintSelection).
60 NSPrintPanel* panel = [NSPrintPanel printPanel];
61 NSPrintInfo* printInfo = print_info_.get();
63 NSPrintPanelOptions options = [panel options];
64 options |= NSPrintPanelShowsPaperSize;
65 options |= NSPrintPanelShowsOrientation;
66 options |= NSPrintPanelShowsScaling;
67 [panel setOptions:options];
69 // Set the print job title text.
71 NSString* job_title = [[parent_view window] title];
73 PMPrintSettings printSettings =
74 (PMPrintSettings)[printInfo PMPrintSettings];
75 PMPrintSettingsSetJobName(printSettings, (CFStringRef)job_title);
76 [printInfo updateFromPMPrintSettings];
80 // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
81 // Will require restructuring the PrintingContext API to use a callback.
82 NSInteger selection = [panel runModalWithPrintInfo:printInfo];
83 if (selection == NSOKButton) {
84 print_info_.reset([[panel printInfo] retain]);
85 InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
88 callback->Run(CANCEL);
92 PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
93 DCHECK(!in_print_job_);
95 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
96 InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
101 PrintingContext::Result PrintingContextMac::UpdatePrintSettings(
102 const DictionaryValue& job_settings, const PageRanges& ranges) {
103 DCHECK(!in_print_job_);
105 // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
106 // with a clean slate.
107 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
115 std::string device_name;
117 if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
118 !job_settings.GetBoolean(kSettingCollate, &collate) ||
119 !job_settings.GetBoolean(kSettingColor, &color) ||
120 !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
121 !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
122 !job_settings.GetInteger(kSettingCopies, &copies) ||
123 !job_settings.GetString(kSettingDeviceName, &device_name)) {
127 bool print_to_cloud = job_settings.HasKey(printing::kSettingCloudPrintId);
129 if (!print_to_pdf && !print_to_cloud) {
130 if (!SetPrinter(device_name))
133 if (!SetCopiesInPrintSettings(copies))
136 if (!SetCollateInPrintSettings(collate))
139 if (!SetDuplexModeInPrintSettings(
140 static_cast<DuplexMode>(duplex_mode))) {
144 if (!SetOutputIsColor(color))
148 if (!SetOrientationIsLandscape(landscape))
151 [print_info_.get() updateFromPMPrintSettings];
153 InitPrintSettingsFromPrintInfo(ranges);
157 void PrintingContextMac::InitPrintSettingsFromPrintInfo(
158 const PageRanges& ranges) {
159 PMPrintSession print_session =
160 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
161 PMPageFormat page_format =
162 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
164 PMSessionGetCurrentPrinter(print_session, &printer);
165 PrintSettingsInitializerMac::InitPrintSettings(
166 printer, page_format, ranges, false, &settings_);
169 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
170 DCHECK(print_info_.get());
171 PMPrintSession print_session =
172 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
174 PMPrinter current_printer;
175 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
178 CFStringRef current_printer_id = PMPrinterGetID(current_printer);
179 if (!current_printer_id)
182 base::mac::ScopedCFTypeRef<CFStringRef> new_printer_id(
183 base::SysUTF8ToCFStringRef(device_name));
184 if (!new_printer_id.get())
187 if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
192 PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
193 if (new_printer == NULL)
196 OSStatus status = PMSessionSetCurrentPMPrinter(print_session, new_printer);
197 PMRelease(new_printer);
198 return status == noErr;
201 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
205 PMPrintSettings pmPrintSettings =
206 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
207 return PMSetCopies(pmPrintSettings, copies, false) == noErr;
210 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
211 PMPrintSettings pmPrintSettings =
212 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
213 return PMSetCollate(pmPrintSettings, collate) == noErr;
216 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
217 PMPageFormat page_format =
218 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
220 PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
222 if (PMSetOrientation(page_format, orientation, false) != noErr)
225 [print_info_.get() updateFromPMPageFormat];
229 bool PrintingContextMac::SetDuplexModeInPrintSettings(DuplexMode mode) {
230 PMDuplexMode duplexSetting;
233 duplexSetting = kPMDuplexNoTumble;
236 duplexSetting = kPMDuplexTumble;
239 duplexSetting = kPMDuplexNone;
243 PMPrintSettings pmPrintSettings =
244 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
245 return PMSetDuplex(pmPrintSettings, duplexSetting) == noErr;
248 bool PrintingContextMac::SetOutputIsColor(bool color) {
249 PMPrintSettings pmPrintSettings =
250 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
251 CFStringRef output_color = color ? NULL : kGrayColor;
253 return PMPrintSettingsSetValue(pmPrintSettings,
259 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
260 PageRanges page_ranges;
261 NSDictionary* print_info_dict = [print_info_.get() dictionary];
262 if (![[print_info_dict objectForKey:NSPrintAllPages] boolValue]) {
264 range.from = [[print_info_dict objectForKey:NSPrintFirstPage] intValue] - 1;
265 range.to = [[print_info_dict objectForKey:NSPrintLastPage] intValue] - 1;
266 page_ranges.push_back(range);
271 PrintingContext::Result PrintingContextMac::InitWithSettings(
272 const PrintSettings& settings) {
273 DCHECK(!in_print_job_);
275 settings_ = settings;
282 PrintingContext::Result PrintingContextMac::NewDocument(
283 const string16& document_name) {
284 DCHECK(!in_print_job_);
286 in_print_job_ = true;
288 PMPrintSession print_session =
289 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
290 PMPrintSettings print_settings =
291 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
292 PMPageFormat page_format =
293 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
295 base::mac::ScopedCFTypeRef<CFStringRef> job_title(
296 base::SysUTF16ToCFStringRef(document_name));
297 PMPrintSettingsSetJobName(print_settings, job_title.get());
299 OSStatus status = PMSessionBeginCGDocumentNoDialog(print_session,
308 PrintingContext::Result PrintingContextMac::NewPage() {
311 DCHECK(in_print_job_);
314 PMPrintSession print_session =
315 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
316 PMPageFormat page_format =
317 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
319 status = PMSessionBeginPageNoDialog(print_session, page_format, NULL);
322 status = PMSessionGetCGGraphicsContext(print_session, &context_);
329 PrintingContext::Result PrintingContextMac::PageDone() {
332 DCHECK(in_print_job_);
335 PMPrintSession print_session =
336 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
337 OSStatus status = PMSessionEndPageNoDialog(print_session);
345 PrintingContext::Result PrintingContextMac::DocumentDone() {
348 DCHECK(in_print_job_);
350 PMPrintSession print_session =
351 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
352 OSStatus status = PMSessionEndDocumentNoDialog(print_session);
360 void PrintingContextMac::Cancel() {
361 abort_printing_ = true;
362 in_print_job_ = false;
365 PMPrintSession print_session =
366 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
367 PMSessionEndPageNoDialog(print_session);
370 void PrintingContextMac::ReleaseContext() {
375 gfx::NativeDrawingContext PrintingContextMac::context() const {
379 } // namespace printing