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(
36 gfx::NativeView parent_view,
39 const PrintSettingsCallback& callback) {
40 // Third-party print drivers seem to be an area prone to raising exceptions.
41 // This will allow exceptions to be raised, but does not handle them. The
42 // NSPrintPanel appears to have appropriate NSException handlers.
43 base::mac::ScopedNSExceptionEnabler enabler;
45 // Exceptions can also happen when the NSPrintPanel is being
46 // deallocated, so it must be autoreleased within this scope.
47 base::mac::ScopedNSAutoreleasePool pool;
49 DCHECK([NSThread isMainThread]);
51 // We deliberately don't feed max_pages into the dialog, because setting
52 // NSPrintLastPage makes the print dialog pre-select the option to only print
55 // TODO(stuartmorgan): implement 'print selection only' (probably requires
56 // adding a new custom view to the panel on 10.5; 10.6 has
57 // NSPrintPanelShowsPrintSelection).
58 NSPrintPanel* panel = [NSPrintPanel printPanel];
59 NSPrintInfo* printInfo = print_info_.get();
61 NSPrintPanelOptions options = [panel options];
62 options |= NSPrintPanelShowsPaperSize;
63 options |= NSPrintPanelShowsOrientation;
64 options |= NSPrintPanelShowsScaling;
65 [panel setOptions:options];
67 // Set the print job title text.
69 NSString* job_title = [[parent_view window] title];
71 PMPrintSettings printSettings =
72 (PMPrintSettings)[printInfo PMPrintSettings];
73 PMPrintSettingsSetJobName(printSettings, (CFStringRef)job_title);
74 [printInfo updateFromPMPrintSettings];
78 // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
79 // Will require restructuring the PrintingContext API to use a callback.
80 NSInteger selection = [panel runModalWithPrintInfo:printInfo];
81 if (selection == NSOKButton) {
82 print_info_.reset([[panel printInfo] retain]);
83 InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
90 PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
91 DCHECK(!in_print_job_);
93 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
94 InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
99 PrintingContext::Result PrintingContextMac::UpdatePrinterSettings(
100 const DictionaryValue& job_settings, const PageRanges& ranges) {
101 DCHECK(!in_print_job_);
103 // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
104 // with a clean slate.
105 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
111 bool is_cloud_dialog;
114 std::string device_name;
116 if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
117 !job_settings.GetBoolean(kSettingCollate, &collate) ||
118 !job_settings.GetInteger(kSettingColor, &color) ||
119 !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
120 !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
121 !job_settings.GetInteger(kSettingCopies, &copies) ||
122 !job_settings.GetString(kSettingDeviceName, &device_name) ||
123 !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
127 bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
128 bool open_pdf_in_preview = job_settings.HasKey(kSettingOpenPDFInPreview);
130 if (!print_to_pdf && !print_to_cloud && !is_cloud_dialog) {
131 if (!SetPrinter(device_name))
134 if (!SetCopiesInPrintSettings(copies))
137 if (!SetCollateInPrintSettings(collate))
140 if (!SetDuplexModeInPrintSettings(
141 static_cast<DuplexMode>(duplex_mode))) {
145 if (!SetOutputColor(color))
148 if (open_pdf_in_preview) {
149 if (!SetPrintPreviewJob())
153 if (!UpdatePageFormatWithPaperInfo())
156 if (!SetOrientationIsLandscape(landscape))
159 [print_info_.get() updateFromPMPrintSettings];
161 InitPrintSettingsFromPrintInfo(ranges);
165 bool PrintingContextMac::SetPrintPreviewJob() {
166 PMPrintSession print_session =
167 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
168 PMPrintSettings print_settings =
169 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
170 return PMSessionSetDestination(
171 print_session, print_settings, kPMDestinationPreview,
172 NULL, NULL) == noErr;
175 void PrintingContextMac::InitPrintSettingsFromPrintInfo(
176 const PageRanges& ranges) {
177 PMPrintSession print_session =
178 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
179 PMPageFormat page_format =
180 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
182 PMSessionGetCurrentPrinter(print_session, &printer);
183 PrintSettingsInitializerMac::InitPrintSettings(
184 printer, page_format, ranges, false, &settings_);
187 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
188 DCHECK(print_info_.get());
189 PMPrintSession print_session =
190 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
192 PMPrinter current_printer;
193 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
196 CFStringRef current_printer_id = PMPrinterGetID(current_printer);
197 if (!current_printer_id)
200 base::mac::ScopedCFTypeRef<CFStringRef> new_printer_id(
201 base::SysUTF8ToCFStringRef(device_name));
202 if (!new_printer_id.get())
205 if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
210 PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
211 if (new_printer == NULL)
214 OSStatus status = PMSessionSetCurrentPMPrinter(print_session, new_printer);
215 PMRelease(new_printer);
216 return status == noErr;
219 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
220 PMPrintSession print_session =
221 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
223 PMPageFormat default_page_format =
224 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
226 PMPaper default_paper;
227 if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr)
230 double default_page_width, default_page_height;
231 if (PMPaperGetWidth(default_paper, &default_page_width) != noErr)
234 if (PMPaperGetHeight(default_paper, &default_page_height) != noErr)
237 PMPrinter current_printer = NULL;
238 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
241 if (current_printer == nil)
244 CFArrayRef paper_list = NULL;
245 if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
248 PMPaper best_matching_paper = kPMNoData;
249 int num_papers = CFArrayGetCount(paper_list);
250 for (int i = 0; i < num_papers; ++i) {
251 PMPaper paper = (PMPaper) [(NSArray* ) paper_list objectAtIndex: i];
252 double paper_width, paper_height;
253 PMPaperGetWidth(paper, &paper_width);
254 PMPaperGetHeight(paper, &paper_height);
255 if (default_page_width == paper_width &&
256 default_page_height == paper_height) {
257 best_matching_paper = paper;
260 // Trying to find the best matching paper.
261 if (fabs(default_page_width - paper_width) < 2 &&
262 fabs(default_page_height - paper_height) < 2) {
263 best_matching_paper = paper;
267 if (best_matching_paper == kPMNoData) {
268 PMPaper paper = kPMNoData;
269 // Create a custom paper for the specified default page size.
270 PMPaperMargins default_margins;
271 if (PMPaperGetMargins(default_paper, &default_margins) != noErr)
274 const PMPaperMargins margins =
275 {default_margins.top, default_margins.left, default_margins.bottom,
276 default_margins.right};
277 CFStringRef paper_id = CFSTR("Custom paper ID");
278 CFStringRef paper_name = CFSTR("Custom paper");
279 if (PMPaperCreateCustom(current_printer, paper_id, paper_name,
280 default_page_width, default_page_height, &margins, &paper) !=
284 [print_info_.get() updateFromPMPageFormat];
287 PMPageFormat chosen_page_format = NULL;
288 if (PMCreatePageFormat((PMPageFormat*) &chosen_page_format) != noErr)
291 // Create page format from that paper.
292 if (PMCreatePageFormatWithPMPaper(&chosen_page_format,
293 best_matching_paper) != noErr) {
294 PMRelease(chosen_page_format);
297 // Copy over the original format with the new page format.
298 if (PMCopyPageFormat(chosen_page_format, default_page_format) != noErr) {
299 PMRelease(chosen_page_format);
302 [print_info_.get() updateFromPMPageFormat];
303 PMRelease(chosen_page_format);
308 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
312 PMPrintSettings pmPrintSettings =
313 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
314 return PMSetCopies(pmPrintSettings, copies, false) == noErr;
317 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
318 PMPrintSettings pmPrintSettings =
319 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
320 return PMSetCollate(pmPrintSettings, collate) == noErr;
323 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
324 PMPageFormat page_format =
325 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
327 PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
329 if (PMSetOrientation(page_format, orientation, false) != noErr)
332 PMPrintSession print_session =
333 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
335 PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
337 [print_info_.get() updateFromPMPageFormat];
341 bool PrintingContextMac::SetDuplexModeInPrintSettings(DuplexMode mode) {
342 PMDuplexMode duplexSetting;
345 duplexSetting = kPMDuplexNoTumble;
348 duplexSetting = kPMDuplexTumble;
351 duplexSetting = kPMDuplexNone;
353 default: // UNKNOWN_DUPLEX_MODE
357 PMPrintSettings pmPrintSettings =
358 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
359 return PMSetDuplex(pmPrintSettings, duplexSetting) == noErr;
362 bool PrintingContextMac::SetOutputColor(int color_mode) {
363 PMPrintSettings pmPrintSettings =
364 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
365 std::string color_setting_name;
366 std::string color_value;
367 GetColorModelForMode(color_mode, &color_setting_name, &color_value);
368 base::mac::ScopedCFTypeRef<CFStringRef> color_setting(
369 base::SysUTF8ToCFStringRef(color_setting_name));
370 base::mac::ScopedCFTypeRef<CFStringRef> output_color(
371 base::SysUTF8ToCFStringRef(color_value));
373 return PMPrintSettingsSetValue(pmPrintSettings,
379 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
380 PageRanges page_ranges;
381 NSDictionary* print_info_dict = [print_info_.get() dictionary];
382 if (![[print_info_dict objectForKey:NSPrintAllPages] boolValue]) {
384 range.from = [[print_info_dict objectForKey:NSPrintFirstPage] intValue] - 1;
385 range.to = [[print_info_dict objectForKey:NSPrintLastPage] intValue] - 1;
386 page_ranges.push_back(range);
391 PrintingContext::Result PrintingContextMac::InitWithSettings(
392 const PrintSettings& settings) {
393 DCHECK(!in_print_job_);
395 settings_ = settings;
402 PrintingContext::Result PrintingContextMac::NewDocument(
403 const string16& document_name) {
404 DCHECK(!in_print_job_);
406 in_print_job_ = true;
408 PMPrintSession print_session =
409 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
410 PMPrintSettings print_settings =
411 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
412 PMPageFormat page_format =
413 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
415 base::mac::ScopedCFTypeRef<CFStringRef> job_title(
416 base::SysUTF16ToCFStringRef(document_name));
417 PMPrintSettingsSetJobName(print_settings, job_title.get());
419 OSStatus status = PMSessionBeginCGDocumentNoDialog(print_session,
428 PrintingContext::Result PrintingContextMac::NewPage() {
431 DCHECK(in_print_job_);
434 PMPrintSession print_session =
435 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
436 PMPageFormat page_format =
437 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
439 status = PMSessionBeginPageNoDialog(print_session, page_format, NULL);
442 status = PMSessionGetCGGraphicsContext(print_session, &context_);
449 PrintingContext::Result PrintingContextMac::PageDone() {
452 DCHECK(in_print_job_);
455 PMPrintSession print_session =
456 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
457 OSStatus status = PMSessionEndPageNoDialog(print_session);
465 PrintingContext::Result PrintingContextMac::DocumentDone() {
468 DCHECK(in_print_job_);
470 PMPrintSession print_session =
471 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
472 OSStatus status = PMSessionEndDocumentNoDialog(print_session);
480 void PrintingContextMac::Cancel() {
481 abort_printing_ = true;
482 in_print_job_ = false;
485 PMPrintSession print_session =
486 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
487 PMSessionEndPageNoDialog(print_session);
490 void PrintingContextMac::ReleaseContext() {
495 gfx::NativeDrawingContext PrintingContextMac::context() const {
499 } // namespace printing