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 <AppKit/AppKit.h>
12 #include "base/logging.h"
13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/mac/scoped_nsautorelease_pool.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "printing/print_settings_initializer_mac.h"
19 #include "printing/units.h"
25 const int kMaxPaperSizeDiffereceInPoints = 2;
27 // Return true if PPD name of paper is equal.
28 bool IsPaperNameEqual(CFStringRef name1, const PMPaper& paper2) {
29 CFStringRef name2 = NULL;
30 return (name1 && PMPaperGetPPDPaperName(paper2, &name2) == noErr) &&
31 (CFStringCompare(name1, name2, kCFCompareCaseInsensitive) ==
35 PMPaper MatchPaper(CFArrayRef paper_list,
39 double best_match = std::numeric_limits<double>::max();
40 PMPaper best_matching_paper = NULL;
41 int num_papers = CFArrayGetCount(paper_list);
42 for (int i = 0; i < num_papers; ++i) {
43 PMPaper paper = (PMPaper)[(NSArray*)paper_list objectAtIndex : i];
44 double paper_width = 0.0;
45 double paper_height = 0.0;
46 PMPaperGetWidth(paper, &paper_width);
47 PMPaperGetHeight(paper, &paper_height);
49 std::max(fabs(width - paper_width), fabs(height - paper_height));
51 // Ignore papers with size too different from expected.
52 if (difference > kMaxPaperSizeDiffereceInPoints)
55 if (name && IsPaperNameEqual(name, paper))
58 if (difference < best_match) {
59 best_matching_paper = paper;
60 best_match = difference;
63 return best_matching_paper;
69 scoped_ptr<PrintingContext> PrintingContext::Create(Delegate* delegate) {
70 return make_scoped_ptr<PrintingContext>(new PrintingContextMac(delegate));
73 PrintingContextMac::PrintingContextMac(Delegate* delegate)
74 : PrintingContext(delegate),
75 print_info_([[NSPrintInfo sharedPrintInfo] copy]),
79 PrintingContextMac::~PrintingContextMac() {
83 void PrintingContextMac::AskUserForSettings(
87 const PrintSettingsCallback& callback) {
88 // Exceptions can also happen when the NSPrintPanel is being
89 // deallocated, so it must be autoreleased within this scope.
90 base::mac::ScopedNSAutoreleasePool pool;
92 DCHECK([NSThread isMainThread]);
94 // We deliberately don't feed max_pages into the dialog, because setting
95 // NSPrintLastPage makes the print dialog pre-select the option to only print
98 // TODO(stuartmorgan): implement 'print selection only' (probably requires
99 // adding a new custom view to the panel on 10.5; 10.6 has
100 // NSPrintPanelShowsPrintSelection).
101 NSPrintPanel* panel = [NSPrintPanel printPanel];
102 NSPrintInfo* printInfo = print_info_.get();
104 NSPrintPanelOptions options = [panel options];
105 options |= NSPrintPanelShowsPaperSize;
106 options |= NSPrintPanelShowsOrientation;
107 options |= NSPrintPanelShowsScaling;
108 [panel setOptions:options];
110 // Set the print job title text.
111 gfx::NativeView parent_view = delegate_->GetParentView();
113 NSString* job_title = [[parent_view window] title];
115 PMPrintSettings printSettings =
116 (PMPrintSettings)[printInfo PMPrintSettings];
117 PMPrintSettingsSetJobName(printSettings, (CFStringRef)job_title);
118 [printInfo updateFromPMPrintSettings];
122 // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
123 // Will require restructuring the PrintingContext API to use a callback.
124 NSInteger selection = [panel runModalWithPrintInfo:printInfo];
125 if (selection == NSOKButton) {
126 print_info_.reset([[panel printInfo] retain]);
127 settings_.set_ranges(GetPageRangesFromPrintInfo());
128 InitPrintSettingsFromPrintInfo();
131 callback.Run(CANCEL);
135 gfx::Size PrintingContextMac::GetPdfPaperSizeDeviceUnits() {
136 // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
137 // with a clean slate.
138 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
139 UpdatePageFormatWithPaperInfo();
141 PMPageFormat page_format =
142 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
144 PMGetAdjustedPaperRect(page_format, &paper_rect);
146 // Device units are in points. Units per inch is 72.
147 gfx::Size physical_size_device_units(
148 (paper_rect.right - paper_rect.left),
149 (paper_rect.bottom - paper_rect.top));
150 DCHECK(settings_.device_units_per_inch() == kPointsPerInch);
151 return physical_size_device_units;
154 PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
155 DCHECK(!in_print_job_);
157 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
158 settings_.set_ranges(GetPageRangesFromPrintInfo());
159 InitPrintSettingsFromPrintInfo();
164 PrintingContext::Result PrintingContextMac::UpdatePrinterSettings(
165 bool external_preview,
166 bool show_system_dialog,
168 DCHECK(!show_system_dialog);
169 DCHECK(!in_print_job_);
171 // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
172 // with a clean slate.
173 print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
175 if (external_preview) {
176 if (!SetPrintPreviewJob())
179 // Don't need this for preview.
180 if (!SetPrinter(base::UTF16ToUTF8(settings_.device_name())) ||
181 !SetCopiesInPrintSettings(settings_.copies()) ||
182 !SetCollateInPrintSettings(settings_.collate()) ||
183 !SetDuplexModeInPrintSettings(settings_.duplex_mode()) ||
184 !SetOutputColor(settings_.color())) {
189 if (!UpdatePageFormatWithPaperInfo() ||
190 !SetOrientationIsLandscape(settings_.landscape())) {
194 [print_info_.get() updateFromPMPrintSettings];
196 InitPrintSettingsFromPrintInfo();
200 bool PrintingContextMac::SetPrintPreviewJob() {
201 PMPrintSession print_session =
202 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
203 PMPrintSettings print_settings =
204 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
205 return PMSessionSetDestination(
206 print_session, print_settings, kPMDestinationPreview,
207 NULL, NULL) == noErr;
210 void PrintingContextMac::InitPrintSettingsFromPrintInfo() {
211 PMPrintSession print_session =
212 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
213 PMPageFormat page_format =
214 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
216 PMSessionGetCurrentPrinter(print_session, &printer);
217 PrintSettingsInitializerMac::InitPrintSettings(
218 printer, page_format, &settings_);
221 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
222 DCHECK(print_info_.get());
223 PMPrintSession print_session =
224 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
226 PMPrinter current_printer;
227 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
230 CFStringRef current_printer_id = PMPrinterGetID(current_printer);
231 if (!current_printer_id)
234 base::ScopedCFTypeRef<CFStringRef> new_printer_id(
235 base::SysUTF8ToCFStringRef(device_name));
236 if (!new_printer_id.get())
239 if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
244 PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
248 OSStatus status = PMSessionSetCurrentPMPrinter(print_session, new_printer);
249 PMRelease(new_printer);
250 return status == noErr;
253 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
254 PMPrintSession print_session =
255 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
257 PMPageFormat default_page_format =
258 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
260 PMPrinter current_printer = NULL;
261 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
264 double page_width = 0.0;
265 double page_height = 0.0;
266 base::ScopedCFTypeRef<CFStringRef> paper_name;
267 PMPaperMargins margins = {0};
269 const PrintSettings::RequestedMedia& media = settings_.requested_media();
270 if (media.IsDefault()) {
271 PMPaper default_paper;
272 if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr ||
273 PMPaperGetWidth(default_paper, &page_width) != noErr ||
274 PMPaperGetHeight(default_paper, &page_height) != noErr) {
278 // Ignore result, because we can continue without following.
279 CFStringRef tmp_paper_name = NULL;
280 PMPaperGetPPDPaperName(default_paper, &tmp_paper_name);
281 PMPaperGetMargins(default_paper, &margins);
282 paper_name.reset(tmp_paper_name, base::scoped_policy::RETAIN);
284 const double kMutiplier = kPointsPerInch / (10.0f * kHundrethsMMPerInch);
285 page_width = media.size_microns.width() * kMutiplier;
286 page_height = media.size_microns.height() * kMutiplier;
287 paper_name.reset(base::SysUTF8ToCFStringRef(media.vendor_id));
290 CFArrayRef paper_list = NULL;
291 if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
294 PMPaper best_matching_paper =
295 MatchPaper(paper_list, paper_name, page_width, page_height);
297 if (best_matching_paper)
298 return UpdatePageFormatWithPaper(best_matching_paper, default_page_format);
300 // Do nothing if unmatched paper was default system paper.
301 if (media.IsDefault())
304 PMPaper paper = NULL;
305 if (PMPaperCreateCustom(current_printer,
306 CFSTR("Custom paper ID"),
307 CFSTR("Custom paper"),
314 bool result = UpdatePageFormatWithPaper(paper, default_page_format);
319 bool PrintingContextMac::UpdatePageFormatWithPaper(PMPaper paper,
320 PMPageFormat page_format) {
321 PMPageFormat new_format = NULL;
322 if (PMCreatePageFormatWithPMPaper(&new_format, paper) != noErr)
324 // Copy over the original format with the new page format.
325 bool result = (PMCopyPageFormat(new_format, page_format) == noErr);
326 [print_info_.get() updateFromPMPageFormat];
327 PMRelease(new_format);
331 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
335 PMPrintSettings pmPrintSettings =
336 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
337 return PMSetCopies(pmPrintSettings, copies, false) == noErr;
340 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
341 PMPrintSettings pmPrintSettings =
342 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
343 return PMSetCollate(pmPrintSettings, collate) == noErr;
346 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
347 PMPageFormat page_format =
348 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
350 PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
352 if (PMSetOrientation(page_format, orientation, false) != noErr)
355 PMPrintSession print_session =
356 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
358 PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
360 [print_info_.get() updateFromPMPageFormat];
364 bool PrintingContextMac::SetDuplexModeInPrintSettings(DuplexMode mode) {
365 PMDuplexMode duplexSetting;
368 duplexSetting = kPMDuplexNoTumble;
371 duplexSetting = kPMDuplexTumble;
374 duplexSetting = kPMDuplexNone;
376 default: // UNKNOWN_DUPLEX_MODE
380 PMPrintSettings pmPrintSettings =
381 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
382 return PMSetDuplex(pmPrintSettings, duplexSetting) == noErr;
385 bool PrintingContextMac::SetOutputColor(int color_mode) {
386 PMPrintSettings pmPrintSettings =
387 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
388 std::string color_setting_name;
389 std::string color_value;
390 GetColorModelForMode(color_mode, &color_setting_name, &color_value);
391 base::ScopedCFTypeRef<CFStringRef> color_setting(
392 base::SysUTF8ToCFStringRef(color_setting_name));
393 base::ScopedCFTypeRef<CFStringRef> output_color(
394 base::SysUTF8ToCFStringRef(color_value));
396 return PMPrintSettingsSetValue(pmPrintSettings,
402 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
403 PageRanges page_ranges;
404 NSDictionary* print_info_dict = [print_info_.get() dictionary];
405 if (![[print_info_dict objectForKey:NSPrintAllPages] boolValue]) {
407 range.from = [[print_info_dict objectForKey:NSPrintFirstPage] intValue] - 1;
408 range.to = [[print_info_dict objectForKey:NSPrintLastPage] intValue] - 1;
409 page_ranges.push_back(range);
414 PrintingContext::Result PrintingContextMac::InitWithSettings(
415 const PrintSettings& settings) {
416 DCHECK(!in_print_job_);
418 settings_ = settings;
425 PrintingContext::Result PrintingContextMac::NewDocument(
426 const base::string16& document_name) {
427 DCHECK(!in_print_job_);
429 in_print_job_ = true;
431 PMPrintSession print_session =
432 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
433 PMPrintSettings print_settings =
434 static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
435 PMPageFormat page_format =
436 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
438 base::ScopedCFTypeRef<CFStringRef> job_title(
439 base::SysUTF16ToCFStringRef(document_name));
440 PMPrintSettingsSetJobName(print_settings, job_title.get());
442 OSStatus status = PMSessionBeginCGDocumentNoDialog(print_session,
451 PrintingContext::Result PrintingContextMac::NewPage() {
454 DCHECK(in_print_job_);
457 PMPrintSession print_session =
458 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
459 PMPageFormat page_format =
460 static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
462 status = PMSessionBeginPageNoDialog(print_session, page_format, NULL);
465 status = PMSessionGetCGGraphicsContext(print_session, &context_);
472 PrintingContext::Result PrintingContextMac::PageDone() {
475 DCHECK(in_print_job_);
478 PMPrintSession print_session =
479 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
480 OSStatus status = PMSessionEndPageNoDialog(print_session);
488 PrintingContext::Result PrintingContextMac::DocumentDone() {
491 DCHECK(in_print_job_);
493 PMPrintSession print_session =
494 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
495 OSStatus status = PMSessionEndDocumentNoDialog(print_session);
503 void PrintingContextMac::Cancel() {
504 abort_printing_ = true;
505 in_print_job_ = false;
508 PMPrintSession print_session =
509 static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
510 PMSessionEndPageNoDialog(print_session);
513 void PrintingContextMac::ReleaseContext() {
518 gfx::NativeDrawingContext PrintingContextMac::context() const {
522 } // namespace printing