Open NaCl IRT file only once at startup
[chromium-blink-merge.git] / printing / printing_context_mac.mm
blob5b11c1898fe3ae5c9db9f5c677673bc1aeb8dff7
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 namespace printing {
20 // static
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]),
28       context_(NULL) {
31 PrintingContextMac::~PrintingContextMac() {
32   ReleaseContext();
35 void PrintingContextMac::AskUserForSettings(gfx::NativeView parent_view,
36                                             int max_pages,
37                                             bool has_selection,
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
52   // a range.
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.
67   if (parent_view) {
68     NSString* job_title = [[parent_view window] title];
69     if (job_title) {
70       PMPrintSettings printSettings =
71           (PMPrintSettings)[printInfo PMPrintSettings];
72       PMPrintSettingsSetJobName(printSettings, (CFStringRef)job_title);
73       [printInfo updateFromPMPrintSettings];
74     }
75   }
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());
83     callback->Run(OK);
84   } else {
85     callback->Run(CANCEL);
86   }
89 PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
90   DCHECK(!in_print_job_);
92   print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
93   InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
95   return OK;
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]);
106   bool collate;
107   int color;
108   bool landscape;
109   bool print_to_pdf;
110   bool is_cloud_dialog;
111   int copies;
112   int duplex_mode;
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)) {
123     return OnError();
124   }
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))
130       return OnError();
132     if (!SetCopiesInPrintSettings(copies))
133       return OnError();
135     if (!SetCollateInPrintSettings(collate))
136       return OnError();
138     if (!SetDuplexModeInPrintSettings(
139             static_cast<DuplexMode>(duplex_mode))) {
140       return OnError();
141     }
143     if (!SetOutputColor(color))
144       return OnError();
145   }
146   if (!UpdatePageFormatWithPaperInfo())
147     return OnError();
149   if (!SetOrientationIsLandscape(landscape))
150     return OnError();
152   [print_info_.get() updateFromPMPrintSettings];
154   InitPrintSettingsFromPrintInfo(ranges);
155   return OK;
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]);
164   PMPrinter printer;
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, &current_printer) != noErr)
177     return false;
179   CFStringRef current_printer_id = PMPrinterGetID(current_printer);
180   if (!current_printer_id)
181     return false;
183   base::mac::ScopedCFTypeRef<CFStringRef> new_printer_id(
184       base::SysUTF8ToCFStringRef(device_name));
185   if (!new_printer_id.get())
186     return false;
188   if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
189           kCFCompareEqualTo) {
190     return true;
191   }
193   PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
194   if (new_printer == NULL)
195     return false;
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)
211     return false;
213   double default_page_width, default_page_height;
214   if (PMPaperGetWidth(default_paper, &default_page_width) != noErr)
215     return false;
217   if (PMPaperGetHeight(default_paper, &default_page_height) != noErr)
218     return false;
220   PMPrinter current_printer = NULL;
221   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
222     return false;
224   if (current_printer == nil)
225     return false;
227   CFArrayRef paper_list = NULL;
228   if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
229     return false;
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;
241       break;
242     }
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;
247     }
248   }
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)
255       return false;
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) !=
264             noErr) {
265       return false;
266     }
267     [print_info_.get() updateFromPMPageFormat];
268     PMRelease(paper);
269   } else {
270     PMPageFormat chosen_page_format = NULL;
271     if (PMCreatePageFormat((PMPageFormat*) &chosen_page_format) != noErr)
272       return false;
274     // Create page format from that paper.
275     if (PMCreatePageFormatWithPMPaper(&chosen_page_format,
276             best_matching_paper) != noErr) {
277       PMRelease(chosen_page_format);
278       return false;
279     }
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);
283       return false;
284     }
285     [print_info_.get() updateFromPMPageFormat];
286     PMRelease(chosen_page_format);
287   }
288   return true;
291 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
292   if (copies < 1)
293     return false;
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)
313     return false;
315   PMPrintSession print_session =
316       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
318   PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
320   [print_info_.get() updateFromPMPageFormat];
321   return true;
324 bool PrintingContextMac::SetDuplexModeInPrintSettings(DuplexMode mode) {
325   PMDuplexMode duplexSetting;
326   switch (mode) {
327     case LONG_EDGE:
328       duplexSetting = kPMDuplexNoTumble;
329       break;
330     case SHORT_EDGE:
331       duplexSetting = kPMDuplexTumble;
332       break;
333     case SIMPLEX:
334       duplexSetting = kPMDuplexNone;
335       break;
336     default:  // UNKNOWN_DUPLEX_MODE
337       return true;
338   }
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,
357                                  color_setting.get(),
358                                  output_color.get(),
359                                  false) == noErr;
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]) {
366     PageRange range;
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);
370   }
371   return page_ranges;
374 PrintingContext::Result PrintingContextMac::InitWithSettings(
375     const PrintSettings& settings) {
376   DCHECK(!in_print_job_);
378   settings_ = settings;
380   NOTIMPLEMENTED();
382   return FAILED;
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,
403                                                      print_settings,
404                                                      page_format);
405   if (status != noErr)
406     return OnError();
408   return OK;
411 PrintingContext::Result PrintingContextMac::NewPage() {
412   if (abort_printing_)
413     return CANCEL;
414   DCHECK(in_print_job_);
415   DCHECK(!context_);
417   PMPrintSession print_session =
418       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
419   PMPageFormat page_format =
420       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
421   OSStatus status;
422   status = PMSessionBeginPageNoDialog(print_session, page_format, NULL);
423   if (status != noErr)
424     return OnError();
425   status = PMSessionGetCGGraphicsContext(print_session, &context_);
426   if (status != noErr)
427     return OnError();
429   return OK;
432 PrintingContext::Result PrintingContextMac::PageDone() {
433   if (abort_printing_)
434     return CANCEL;
435   DCHECK(in_print_job_);
436   DCHECK(context_);
438   PMPrintSession print_session =
439       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
440   OSStatus status = PMSessionEndPageNoDialog(print_session);
441   if (status != noErr)
442     OnError();
443   context_ = NULL;
445   return OK;
448 PrintingContext::Result PrintingContextMac::DocumentDone() {
449   if (abort_printing_)
450     return CANCEL;
451   DCHECK(in_print_job_);
453   PMPrintSession print_session =
454       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
455   OSStatus status = PMSessionEndDocumentNoDialog(print_session);
456   if (status != noErr)
457     OnError();
459   ResetSettings();
460   return OK;
463 void PrintingContextMac::Cancel() {
464   abort_printing_ = true;
465   in_print_job_ = false;
466   context_ = NULL;
468   PMPrintSession print_session =
469       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
470   PMSessionEndPageNoDialog(print_session);
473 void PrintingContextMac::ReleaseContext() {
474   print_info_.reset();
475   context_ = NULL;
478 gfx::NativeDrawingContext PrintingContextMac::context() const {
479   return context_;
482 }  // namespace printing