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"
21 PrintingContext* PrintingContext::Create(const std::string& app_locale) {
22 return static_cast<PrintingContext*>(new PrintingContextMac(app_locale));
25 PrintingContextMac::PrintingContextMac(const std::string& app_locale)
26 : PrintingContext(app_locale),
27 print_info_([[NSPrintInfo sharedPrintInfo] copy]),
31 PrintingContextMac::~PrintingContextMac() {
35 void PrintingContextMac::AskUserForSettings(gfx::NativeView parent_view,
38 PrintSettingsCallback* callback) {
39 // Third-party print drivers seem to be an area prone to raising exceptions.
40 // This will allow exceptions to be raised, but does not handle them. The
41 // NSPrintPanel appears to have appropriate NSException handlers.
42 base::mac::ScopedNSExceptionEnabler enabler;
44 // Exceptions can also happen when the NSPrintPanel is being
45 // deallocated, so it must be autoreleased within this scope.
46 base::mac::ScopedNSAutoreleasePool pool;
48 DCHECK([NSThread isMainThread]);
50 // We deliberately don't feed max_pages into the dialog, because setting
51 // NSPrintLastPage makes the print dialog pre-select the option to only print
54 // TODO(stuartmorgan): implement 'print selection only' (probably requires
55 // adding a new custom view to the panel on 10.5; 10.6 has
56 // NSPrintPanelShowsPrintSelection).
57 NSPrintPanel* panel = [NSPrintPanel printPanel];
58 NSPrintInfo* printInfo = print_info_.get();
60 NSPrintPanelOptions options = [panel options];
61 options |= NSPrintPanelShowsPaperSize;
62 options |= NSPrintPanelShowsOrientation;
63 options |= NSPrintPanelShowsScaling;
64 [panel setOptions:options];
66 // Set the print job title text.
68 NSString* job_title = [[parent_view window] title];
70 PMPrintSettings printSettings =
71 (PMPrintSettings)[printInfo PMPrintSettings];
72 PMPrintSettingsSetJobName(printSettings, (CFStringRef)job_title);
73 [printInfo updateFromPMPrintSettings];
77 // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
78 // Will require restructuring the PrintingContext API to use a callback.
79 NSInteger selection = [panel runModalWithPrintInfo:printInfo];
80 if (selection == NSOKButton) {
81 print_info_.reset([[panel printInfo] retain]);
82 InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
85 callback->Run(CANCEL);
89 PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
90 DCHECK(!in_print_job_);
92 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
93 InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
98 PrintingContext::Result PrintingContextMac::UpdatePrinterSettings(
99 const DictionaryValue& job_settings, const PageRanges& ranges) {
100 DCHECK(!in_print_job_);
102 // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
103 // with a clean slate.
104 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
110 bool is_cloud_dialog;
113 std::string device_name;
115 if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
116 !job_settings.GetBoolean(kSettingCollate, &collate) ||
117 !job_settings.GetInteger(kSettingColor, &color) ||
118 !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
119 !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
120 !job_settings.GetInteger(kSettingCopies, &copies) ||
121 !job_settings.GetString(kSettingDeviceName, &device_name) ||
122 !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
126 bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
128 if (!print_to_pdf && !print_to_cloud && !is_cloud_dialog) {
129 if (!SetPrinter(device_name))
132 if (!SetCopiesInPrintSettings(copies))
135 if (!SetCollateInPrintSettings(collate))
138 if (!SetDuplexModeInPrintSettings(
139 static_cast<DuplexMode>(duplex_mode))) {
143 if (!SetOutputColor(color))
146 if (!UpdatePageFormatWithPaperInfo())
149 if (!SetOrientationIsLandscape(landscape))
152 [print_info_.get() updateFromPMPrintSettings];
154 InitPrintSettingsFromPrintInfo(ranges);
158 void PrintingContextMac::InitPrintSettingsFromPrintInfo(
159 const PageRanges& ranges) {
160 PMPrintSession print_session =
161 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
162 PMPageFormat page_format =
163 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
165 PMSessionGetCurrentPrinter(print_session, &printer);
166 PrintSettingsInitializerMac::InitPrintSettings(
167 printer, page_format, ranges, false, &settings_);
170 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
171 DCHECK(print_info_.get());
172 PMPrintSession print_session =
173 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
175 PMPrinter current_printer;
176 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
179 CFStringRef current_printer_id = PMPrinterGetID(current_printer);
180 if (!current_printer_id)
183 base::mac::ScopedCFTypeRef<CFStringRef> new_printer_id(
184 base::SysUTF8ToCFStringRef(device_name));
185 if (!new_printer_id.get())
188 if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
193 PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
194 if (new_printer == NULL)
197 OSStatus status = PMSessionSetCurrentPMPrinter(print_session, new_printer);
198 PMRelease(new_printer);
199 return status == noErr;
202 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
203 PMPrintSession print_session =
204 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
206 PMPageFormat default_page_format =
207 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
209 PMPaper default_paper;
210 if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr)
213 double default_page_width, default_page_height;
214 if (PMPaperGetWidth(default_paper, &default_page_width) != noErr)
217 if (PMPaperGetHeight(default_paper, &default_page_height) != noErr)
220 PMPrinter current_printer = NULL;
221 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
224 if (current_printer == nil)
227 CFArrayRef paper_list = NULL;
228 if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
231 PMPaper best_matching_paper = kPMNoData;
232 int num_papers = CFArrayGetCount(paper_list);
233 for (int i = 0; i < num_papers; ++i) {
234 PMPaper paper = (PMPaper) [(NSArray* ) paper_list objectAtIndex: i];
235 double paper_width, paper_height;
236 PMPaperGetWidth(paper, &paper_width);
237 PMPaperGetHeight(paper, &paper_height);
238 if (default_page_width == paper_width &&
239 default_page_height == paper_height) {
240 best_matching_paper = paper;
243 // Trying to find the best matching paper.
244 if (fabs(default_page_width - paper_width) < 2 &&
245 fabs(default_page_height - paper_height) < 2) {
246 best_matching_paper = paper;
250 if (best_matching_paper == kPMNoData) {
251 PMPaper paper = kPMNoData;
252 // Create a custom paper for the specified default page size.
253 PMPaperMargins default_margins;
254 if (PMPaperGetMargins(default_paper, &default_margins) != noErr)
257 const PMPaperMargins margins =
258 {default_margins.top, default_margins.left, default_margins.bottom,
259 default_margins.right};
260 CFStringRef paper_id = CFSTR("Custom paper ID");
261 CFStringRef paper_name = CFSTR("Custom paper");
262 if (PMPaperCreateCustom(current_printer, paper_id, paper_name,
263 default_page_width, default_page_height, &margins, &paper) !=
267 [print_info_.get() updateFromPMPageFormat];
270 PMPageFormat chosen_page_format = NULL;
271 if (PMCreatePageFormat((PMPageFormat*) &chosen_page_format) != noErr)
274 // Create page format from that paper.
275 if (PMCreatePageFormatWithPMPaper(&chosen_page_format,
276 best_matching_paper) != noErr) {
277 PMRelease(chosen_page_format);
280 // Copy over the original format with the new page format.
281 if (PMCopyPageFormat(chosen_page_format, default_page_format) != noErr) {
282 PMRelease(chosen_page_format);
285 [print_info_.get() updateFromPMPageFormat];
286 PMRelease(chosen_page_format);
291 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
295 PMPrintSettings pmPrintSettings =
296 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
297 return PMSetCopies(pmPrintSettings, copies, false) == noErr;
300 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
301 PMPrintSettings pmPrintSettings =
302 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
303 return PMSetCollate(pmPrintSettings, collate) == noErr;
306 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
307 PMPageFormat page_format =
308 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
310 PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
312 if (PMSetOrientation(page_format, orientation, false) != noErr)
315 PMPrintSession print_session =
316 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
318 PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
320 [print_info_.get() updateFromPMPageFormat];
324 bool PrintingContextMac::SetDuplexModeInPrintSettings(DuplexMode mode) {
325 PMDuplexMode duplexSetting;
328 duplexSetting = kPMDuplexNoTumble;
331 duplexSetting = kPMDuplexTumble;
334 duplexSetting = kPMDuplexNone;
336 default: // UNKNOWN_DUPLEX_MODE
340 PMPrintSettings pmPrintSettings =
341 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
342 return PMSetDuplex(pmPrintSettings, duplexSetting) == noErr;
345 bool PrintingContextMac::SetOutputColor(int color_mode) {
346 PMPrintSettings pmPrintSettings =
347 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
348 std::string color_setting_name;
349 std::string color_value;
350 GetColorModelForMode(color_mode, &color_setting_name, &color_value);
351 base::mac::ScopedCFTypeRef<CFStringRef> color_setting(
352 base::SysUTF8ToCFStringRef(color_setting_name));
353 base::mac::ScopedCFTypeRef<CFStringRef> output_color(
354 base::SysUTF8ToCFStringRef(color_value));
356 return PMPrintSettingsSetValue(pmPrintSettings,
362 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
363 PageRanges page_ranges;
364 NSDictionary* print_info_dict = [print_info_.get() dictionary];
365 if (![[print_info_dict objectForKey:NSPrintAllPages] boolValue]) {
367 range.from = [[print_info_dict objectForKey:NSPrintFirstPage] intValue] - 1;
368 range.to = [[print_info_dict objectForKey:NSPrintLastPage] intValue] - 1;
369 page_ranges.push_back(range);
374 PrintingContext::Result PrintingContextMac::InitWithSettings(
375 const PrintSettings& settings) {
376 DCHECK(!in_print_job_);
378 settings_ = settings;
385 PrintingContext::Result PrintingContextMac::NewDocument(
386 const string16& document_name) {
387 DCHECK(!in_print_job_);
389 in_print_job_ = true;
391 PMPrintSession print_session =
392 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
393 PMPrintSettings print_settings =
394 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
395 PMPageFormat page_format =
396 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
398 base::mac::ScopedCFTypeRef<CFStringRef> job_title(
399 base::SysUTF16ToCFStringRef(document_name));
400 PMPrintSettingsSetJobName(print_settings, job_title.get());
402 OSStatus status = PMSessionBeginCGDocumentNoDialog(print_session,
411 PrintingContext::Result PrintingContextMac::NewPage() {
414 DCHECK(in_print_job_);
417 PMPrintSession print_session =
418 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
419 PMPageFormat page_format =
420 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
422 status = PMSessionBeginPageNoDialog(print_session, page_format, NULL);
425 status = PMSessionGetCGGraphicsContext(print_session, &context_);
432 PrintingContext::Result PrintingContextMac::PageDone() {
435 DCHECK(in_print_job_);
438 PMPrintSession print_session =
439 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
440 OSStatus status = PMSessionEndPageNoDialog(print_session);
448 PrintingContext::Result PrintingContextMac::DocumentDone() {
451 DCHECK(in_print_job_);
453 PMPrintSession print_session =
454 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
455 OSStatus status = PMSessionEndDocumentNoDialog(print_session);
463 void PrintingContextMac::Cancel() {
464 abort_printing_ = true;
465 in_print_job_ = false;
468 PMPrintSession print_session =
469 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
470 PMSessionEndPageNoDialog(print_session);
473 void PrintingContextMac::ReleaseContext() {
478 gfx::NativeDrawingContext PrintingContextMac::context() const {
482 } // namespace printing