Enable chrome with aura for tizen
[platform/framework/web/chromium-efl.git] / printing / printing_context_mac.mm
1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "printing/printing_context_mac.h"
6
7 #import <AppKit/AppKit.h>
8 #import <QuartzCore/QuartzCore.h>
9 #include <cups/cups.h>
10
11 #import <iomanip>
12 #import <numeric>
13
14 #include "base/check.h"
15 #include "base/mac/scoped_cftyperef.h"
16 #include "base/strings/string_piece.h"
17 #include "base/strings/sys_string_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "build/build_config.h"
21 #include "printing/buildflags/buildflags.h"
22 #include "printing/metafile.h"
23 #include "printing/mojom/print.mojom.h"
24 #include "printing/print_settings_initializer_mac.h"
25 #include "printing/printing_features.h"
26 #include "printing/units.h"
27
28 namespace printing {
29
30 namespace {
31
32 const int kMaxPaperSizeDiffereceInPoints = 2;
33
34 // Return true if PPD name of paper is equal.
35 bool IsPaperNameEqual(CFStringRef name1, const PMPaper& paper2) {
36   CFStringRef name2 = nullptr;
37   return (name1 && PMPaperGetPPDPaperName(paper2, &name2) == noErr) &&
38          (CFStringCompare(name1, name2, kCFCompareCaseInsensitive) ==
39           kCFCompareEqualTo);
40 }
41
42 PMPaper MatchPaper(CFArrayRef paper_list,
43                    CFStringRef name,
44                    double width,
45                    double height) {
46   double best_match = std::numeric_limits<double>::max();
47   PMPaper best_matching_paper = nullptr;
48   int num_papers = CFArrayGetCount(paper_list);
49   for (int i = 0; i < num_papers; ++i) {
50     PMPaper paper = (PMPaper)((NSArray*)paper_list)[i];
51     double paper_width = 0.0;
52     double paper_height = 0.0;
53     PMPaperGetWidth(paper, &paper_width);
54     PMPaperGetHeight(paper, &paper_height);
55     double difference =
56         std::max(fabs(width - paper_width), fabs(height - paper_height));
57
58     // Ignore papers with size too different from expected.
59     if (difference > kMaxPaperSizeDiffereceInPoints)
60       continue;
61
62     if (name && IsPaperNameEqual(name, paper))
63       return paper;
64
65     if (difference < best_match) {
66       best_matching_paper = paper;
67       best_match = difference;
68     }
69   }
70   return best_matching_paper;
71 }
72
73 }  // namespace
74
75 // static
76 std::unique_ptr<PrintingContext> PrintingContext::CreateImpl(
77     Delegate* delegate,
78     bool skip_system_calls) {
79   auto context = std::make_unique<PrintingContextMac>(delegate);
80 #if BUILDFLAG(ENABLE_OOP_PRINTING)
81   if (skip_system_calls)
82     context->set_skip_system_calls();
83 #endif
84   return context;
85 }
86
87 PrintingContextMac::PrintingContextMac(Delegate* delegate)
88     : PrintingContext(delegate),
89       print_info_([[NSPrintInfo sharedPrintInfo] copy]),
90       context_(nullptr) {}
91
92 PrintingContextMac::~PrintingContextMac() {
93   ReleaseContext();
94 }
95
96 void PrintingContextMac::AskUserForSettings(int max_pages,
97                                             bool has_selection,
98                                             bool is_scripted,
99                                             PrintSettingsCallback callback) {
100   // Exceptions can also happen when the NSPrintPanel is being
101   // deallocated, so it must be autoreleased within this scope.
102   @autoreleasepool {
103     DCHECK([NSThread isMainThread]);
104
105     // We deliberately don't feed max_pages into the dialog, because setting
106     // NSPrintLastPage makes the print dialog pre-select the option to only
107     // print a range.
108
109     // TODO(stuartmorgan): implement 'print selection only' (probably requires
110     // adding a new custom view to the panel on 10.5; 10.6 has
111     // NSPrintPanelShowsPrintSelection).
112     NSPrintPanel* panel = [NSPrintPanel printPanel];
113     NSPrintInfo* print_info = print_info_.get();
114
115     NSPrintPanelOptions options = [panel options];
116     options |= NSPrintPanelShowsPaperSize;
117     options |= NSPrintPanelShowsOrientation;
118     options |= NSPrintPanelShowsScaling;
119     [panel setOptions:options];
120
121     // Set the print job title text.
122     gfx::NativeView parent_view = delegate_->GetParentView();
123     if (parent_view) {
124       NSString* job_title = [[parent_view.GetNativeNSView() window] title];
125       if (job_title) {
126         PMPrintSettings print_settings =
127             (PMPrintSettings)[print_info PMPrintSettings];
128         PMPrintSettingsSetJobName(print_settings, (CFStringRef)job_title);
129         [print_info updateFromPMPrintSettings];
130       }
131     }
132
133     // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
134     // Will require restructuring the PrintingContext API to use a callback.
135
136     // This function may be called in the middle of a CATransaction, where
137     // running a modal panel is forbidden. That situation isn't ideal, but from
138     // this code's POV the right answer is to defer running the panel until
139     // after the current transaction. See https://crbug.com/849538.
140     __block auto block_callback = std::move(callback);
141     [CATransaction setCompletionBlock:^{
142       NSInteger selection = [panel runModalWithPrintInfo:print_info];
143       if (selection == NSModalResponseOK) {
144         print_info_.reset([[panel printInfo] retain]);
145         settings_->set_ranges(GetPageRangesFromPrintInfo());
146         InitPrintSettingsFromPrintInfo();
147         std::move(block_callback).Run(mojom::ResultCode::kSuccess);
148       } else {
149         std::move(block_callback).Run(mojom::ResultCode::kCanceled);
150       }
151     }];
152   }
153 }
154
155 gfx::Size PrintingContextMac::GetPdfPaperSizeDeviceUnits() {
156   // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
157   // with a clean slate.
158   print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
159   UpdatePageFormatWithPaperInfo();
160
161   PMPageFormat page_format =
162       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
163   PMRect paper_rect;
164   PMGetAdjustedPaperRect(page_format, &paper_rect);
165
166   // Device units are in points. Units per inch is 72.
167   gfx::Size physical_size_device_units((paper_rect.right - paper_rect.left),
168                                        (paper_rect.bottom - paper_rect.top));
169   DCHECK(settings_->device_units_per_inch() == kPointsPerInch);
170   return physical_size_device_units;
171 }
172
173 mojom::ResultCode PrintingContextMac::UseDefaultSettings() {
174   DCHECK(!in_print_job_);
175
176   print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
177   settings_->set_ranges(GetPageRangesFromPrintInfo());
178   InitPrintSettingsFromPrintInfo();
179
180   return mojom::ResultCode::kSuccess;
181 }
182
183 mojom::ResultCode PrintingContextMac::UpdatePrinterSettings(
184     const PrinterSettings& printer_settings) {
185   DCHECK(!printer_settings.show_system_dialog);
186   DCHECK(!in_print_job_);
187
188   // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
189   // with a clean slate.
190   print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
191
192   if (printer_settings.external_preview) {
193     if (!SetPrintPreviewJob())
194       return OnError();
195   } else {
196     // Don't need this for preview.
197     if (!SetPrinter(base::UTF16ToUTF8(settings_->device_name())) ||
198         !SetCopiesInPrintSettings(settings_->copies()) ||
199         !SetCollateInPrintSettings(settings_->collate()) ||
200         !SetDuplexModeInPrintSettings(settings_->duplex_mode()) ||
201         !SetOutputColor(static_cast<int>(settings_->color())) ||
202         !SetResolution(settings_->dpi_size())) {
203       return OnError();
204     }
205   }
206
207   if (!UpdatePageFormatWithPaperInfo() ||
208       !SetOrientationIsLandscape(settings_->landscape())) {
209     return OnError();
210   }
211
212   [print_info_.get() updateFromPMPrintSettings];
213
214   InitPrintSettingsFromPrintInfo();
215   return mojom::ResultCode::kSuccess;
216 }
217
218 bool PrintingContextMac::SetPrintPreviewJob() {
219   PMPrintSession print_session =
220       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
221   PMPrintSettings print_settings =
222       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
223   return PMSessionSetDestination(print_session, print_settings,
224                                  kPMDestinationPreview, nullptr,
225                                  nullptr) == noErr;
226 }
227
228 void PrintingContextMac::InitPrintSettingsFromPrintInfo() {
229   PMPrintSession print_session =
230       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
231   PMPageFormat page_format =
232       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
233   PMPrinter printer;
234   PMSessionGetCurrentPrinter(print_session, &printer);
235   PrintSettingsInitializerMac::InitPrintSettings(printer, page_format,
236                                                  settings_.get());
237 }
238
239 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
240   DCHECK(print_info_.get());
241   PMPrintSession print_session =
242       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
243
244   PMPrinter current_printer;
245   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
246     return false;
247
248   CFStringRef current_printer_id = PMPrinterGetID(current_printer);
249   if (!current_printer_id)
250     return false;
251
252   base::ScopedCFTypeRef<CFStringRef> new_printer_id(
253       base::SysUTF8ToCFStringRef(device_name));
254   if (!new_printer_id.get())
255     return false;
256
257   if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
258       kCFCompareEqualTo) {
259     return true;
260   }
261
262   PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
263   if (!new_printer)
264     return false;
265
266   OSStatus status = PMSessionSetCurrentPMPrinter(print_session, new_printer);
267   PMRelease(new_printer);
268   return status == noErr;
269 }
270
271 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
272   PMPrintSession print_session =
273       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
274
275   PMPageFormat default_page_format =
276       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
277
278   PMPrinter current_printer = nullptr;
279   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
280     return false;
281
282   double page_width = 0.0;
283   double page_height = 0.0;
284   base::ScopedCFTypeRef<CFStringRef> paper_name;
285   PMPaperMargins margins = {0};
286
287   const PrintSettings::RequestedMedia& media = settings_->requested_media();
288   if (media.IsDefault()) {
289     PMPaper default_paper;
290     if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr ||
291         PMPaperGetWidth(default_paper, &page_width) != noErr ||
292         PMPaperGetHeight(default_paper, &page_height) != noErr) {
293       return false;
294     }
295
296     // Ignore result, because we can continue without following.
297     CFStringRef tmp_paper_name = nullptr;
298     PMPaperGetPPDPaperName(default_paper, &tmp_paper_name);
299     PMPaperGetMargins(default_paper, &margins);
300     paper_name.reset(tmp_paper_name, base::scoped_policy::RETAIN);
301   } else {
302     const double kMutiplier =
303         kPointsPerInch / static_cast<float>(kMicronsPerInch);
304     page_width = media.size_microns.width() * kMutiplier;
305     page_height = media.size_microns.height() * kMutiplier;
306     paper_name.reset(base::SysUTF8ToCFStringRef(media.vendor_id));
307   }
308
309   CFArrayRef paper_list = nullptr;
310   if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
311     return false;
312
313   PMPaper best_matching_paper =
314       MatchPaper(paper_list, paper_name, page_width, page_height);
315
316   if (best_matching_paper)
317     return UpdatePageFormatWithPaper(best_matching_paper, default_page_format);
318
319   // Do nothing if unmatched paper was default system paper.
320   if (media.IsDefault())
321     return true;
322
323   PMPaper paper = nullptr;
324   if (PMPaperCreateCustom(current_printer, CFSTR("Custom paper ID"),
325                           CFSTR("Custom paper"), page_width, page_height,
326                           &margins, &paper) != noErr) {
327     return false;
328   }
329   bool result = UpdatePageFormatWithPaper(paper, default_page_format);
330   PMRelease(paper);
331   return result;
332 }
333
334 bool PrintingContextMac::UpdatePageFormatWithPaper(PMPaper paper,
335                                                    PMPageFormat page_format) {
336   PMPageFormat new_format = nullptr;
337   if (PMCreatePageFormatWithPMPaper(&new_format, paper) != noErr)
338     return false;
339   // Copy over the original format with the new page format.
340   bool result = (PMCopyPageFormat(new_format, page_format) == noErr);
341   [print_info_.get() updateFromPMPageFormat];
342   PMRelease(new_format);
343   return result;
344 }
345
346 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
347   if (copies < 1)
348     return false;
349
350   PMPrintSettings print_settings =
351       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
352   return PMSetCopies(print_settings, copies, false) == noErr;
353 }
354
355 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
356   PMPrintSettings print_settings =
357       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
358   return PMSetCollate(print_settings, collate) == noErr;
359 }
360
361 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
362   PMPageFormat page_format =
363       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
364
365   PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
366
367   if (PMSetOrientation(page_format, orientation, false) != noErr)
368     return false;
369
370   PMPrintSession print_session =
371       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
372
373   PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
374
375   [print_info_.get() updateFromPMPageFormat];
376   return true;
377 }
378
379 bool PrintingContextMac::SetDuplexModeInPrintSettings(mojom::DuplexMode mode) {
380   PMDuplexMode duplexSetting;
381   switch (mode) {
382     case mojom::DuplexMode::kLongEdge:
383       duplexSetting = kPMDuplexNoTumble;
384       break;
385     case mojom::DuplexMode::kShortEdge:
386       duplexSetting = kPMDuplexTumble;
387       break;
388     case mojom::DuplexMode::kSimplex:
389       duplexSetting = kPMDuplexNone;
390       break;
391     default:  // kUnknownDuplexMode
392       return true;
393   }
394
395   PMPrintSettings print_settings =
396       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
397   return PMSetDuplex(print_settings, duplexSetting) == noErr;
398 }
399
400 bool PrintingContextMac::SetOutputColor(int color_mode) {
401   const mojom::ColorModel color_model = ColorModeToColorModel(color_mode);
402
403   if (!base::FeatureList::IsEnabled(features::kCupsIppPrintingBackend)) {
404     std::string color_setting_name;
405     std::string color_value;
406     GetColorModelForModel(color_model, &color_setting_name, &color_value);
407     return SetKeyValue(color_setting_name, color_value);
408   }
409
410   // First, set the default CUPS IPP output color.
411   if (!SetKeyValue(CUPS_PRINT_COLOR_MODE,
412                    GetIppColorModelForModel(color_model))) {
413     return false;
414   }
415
416   struct PpdColorSetting {
417     constexpr PpdColorSetting(base::StringPiece name,
418                               base::StringPiece bw,
419                               base::StringPiece color)
420         : name(name), bw(bw), color(color) {}
421     base::StringPiece name;
422     base::StringPiece bw;
423     base::StringPiece color;
424   };
425
426   // TODO(crbug.com/1210992): Move `kKnownPpdColorSettings` elsewhere so it can
427   // be used for general CUPS printing code (e.g., for parsing PPDs).
428   static constexpr PpdColorSetting kKnownPpdColorSettings[] = {
429       {"ARCMode", "CMBW", "CMColor"},                         // Sharp
430       {"BLW", "TrueM", "FalseM"},                             // Lexmark
431       {"BRMonoColor", "Mono", "FullColor"},                   // Brother
432       {"BRPrintQuality", "Black", "Color"},                   // Brother
433       {"CNIJGrayScale", "1", "0"},                            // Canon
434       {"ColorMode", "Monochrome", "Color"},                   // Samsung
435       {"ColorModel", "Gray", "Color"},                        // Generic
436       {"HPColorMode", "GrayscalePrint", "ColorPrint"},        // HP
437       {"Ink", "MONO", "COLOR"},                               // Epson
438       {"OKControl", "Gray", "Auto"},                          // Oki
439       {"PrintoutMode", "Normal.Gray", "Normal"},              // Foomatic
440       {"SelectColor", "Grayscale", "Color"},                  // Konica Minolta
441       {"XRXColor", "BW", "Automatic"},                        // Xerox
442       {"XROutputColor", "PrintAsGrayscale", "PrintAsColor"},  // Xerox
443   };
444
445   // Even when interfacing with printer settings using CUPS IPP, the print job
446   // may still expect PPD color values if the printer was added to the system
447   // with a PPD. To avoid parsing PPDs (which is the point of using CUPS IPP),
448   // set every single known PPD color setting and hope that one of them sticks.
449   const bool is_color = IsColorModelSelected(color_model).value_or(false);
450   for (const auto& setting : kKnownPpdColorSettings) {
451     const base::StringPiece& color_setting_name = setting.name;
452     const base::StringPiece& color_value =
453         is_color ? setting.color : setting.bw;
454     if (!SetKeyValue(color_setting_name, color_value))
455       return false;
456   }
457
458   return true;
459 }
460
461 bool PrintingContextMac::SetResolution(const gfx::Size& dpi_size) {
462   if (dpi_size.IsEmpty())
463     return true;
464
465   PMPrintSession print_session =
466       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
467   PMPrinter current_printer;
468   if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
469     return false;
470
471   PMResolution resolution;
472   resolution.hRes = dpi_size.width();
473   resolution.vRes = dpi_size.height();
474
475   PMPrintSettings print_settings =
476       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
477   return PMPrinterSetOutputResolution(current_printer, print_settings,
478                                       &resolution) == noErr;
479 }
480
481 bool PrintingContextMac::SetKeyValue(base::StringPiece key,
482                                      base::StringPiece value) {
483   PMPrintSettings print_settings =
484       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
485   base::ScopedCFTypeRef<CFStringRef> cf_key(base::SysUTF8ToCFStringRef(key));
486   base::ScopedCFTypeRef<CFStringRef> cf_value(
487       base::SysUTF8ToCFStringRef(value));
488
489   return PMPrintSettingsSetValue(print_settings, cf_key.get(), cf_value.get(),
490                                  /*locked=*/false) == noErr;
491 }
492
493 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
494   PageRanges page_ranges;
495   NSDictionary* print_info_dict = [print_info_.get() dictionary];
496   if (![print_info_dict[NSPrintAllPages] boolValue]) {
497     PageRange range;
498     range.from = [print_info_dict[NSPrintFirstPage] intValue] - 1;
499     range.to = [print_info_dict[NSPrintLastPage] intValue] - 1;
500     page_ranges.push_back(range);
501   }
502   return page_ranges;
503 }
504
505 mojom::ResultCode PrintingContextMac::NewDocument(
506     const std::u16string& document_name) {
507   DCHECK(!in_print_job_);
508
509   in_print_job_ = true;
510
511   if (skip_system_calls())
512     return mojom::ResultCode::kSuccess;
513
514   PMPrintSession print_session =
515       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
516   PMPrintSettings print_settings =
517       static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
518   PMPageFormat page_format =
519       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
520
521   base::ScopedCFTypeRef<CFStringRef> job_title(
522       base::SysUTF16ToCFStringRef(document_name));
523   PMPrintSettingsSetJobName(print_settings, job_title.get());
524
525   OSStatus status = PMSessionBeginCGDocumentNoDialog(
526       print_session, print_settings, page_format);
527   if (status != noErr)
528     return OnError();
529
530   return mojom::ResultCode::kSuccess;
531 }
532
533 mojom::ResultCode PrintingContextMac::NewPage() {
534   if (abort_printing_)
535     return mojom::ResultCode::kCanceled;
536   DCHECK(in_print_job_);
537   DCHECK(!context_);
538
539   PMPrintSession print_session =
540       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
541   PMPageFormat page_format =
542       static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
543   OSStatus status;
544   status = PMSessionBeginPageNoDialog(print_session, page_format, nullptr);
545   if (status != noErr)
546     return OnError();
547   status = PMSessionGetCGGraphicsContext(print_session, &context_);
548   if (status != noErr)
549     return OnError();
550
551   return mojom::ResultCode::kSuccess;
552 }
553
554 mojom::ResultCode PrintingContextMac::PageDone() {
555   if (abort_printing_)
556     return mojom::ResultCode::kCanceled;
557   DCHECK(in_print_job_);
558   DCHECK(context_);
559
560   PMPrintSession print_session =
561       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
562   OSStatus status = PMSessionEndPageNoDialog(print_session);
563   if (status != noErr)
564     OnError();
565   context_ = nullptr;
566
567   return mojom::ResultCode::kSuccess;
568 }
569
570 mojom::ResultCode PrintingContextMac::PrintDocument(
571     const MetafilePlayer& metafile,
572     const PrintSettings& settings,
573     uint32_t num_pages) {
574   const PageSetup& page_setup = settings.page_setup_device_units();
575   const CGRect paper_rect = gfx::Rect(page_setup.physical_size()).ToCGRect();
576
577   for (size_t metafile_page_number = 1; metafile_page_number <= num_pages;
578        metafile_page_number++) {
579     mojom::ResultCode result = NewPage();
580     if (result != mojom::ResultCode::kSuccess)
581       return result;
582     if (!metafile.RenderPage(metafile_page_number, context_, paper_rect,
583                              /*autorotate=*/true, /*fit_to_page=*/false)) {
584       return mojom::ResultCode::kFailed;
585     }
586     result = PageDone();
587     if (result != mojom::ResultCode::kSuccess)
588       return result;
589   }
590   return mojom::ResultCode::kSuccess;
591 }
592
593 mojom::ResultCode PrintingContextMac::DocumentDone() {
594   if (abort_printing_)
595     return mojom::ResultCode::kCanceled;
596   DCHECK(in_print_job_);
597
598   PMPrintSession print_session =
599       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
600   OSStatus status = PMSessionEndDocumentNoDialog(print_session);
601   if (status != noErr)
602     OnError();
603
604   ResetSettings();
605   return mojom::ResultCode::kSuccess;
606 }
607
608 void PrintingContextMac::Cancel() {
609   abort_printing_ = true;
610   in_print_job_ = false;
611   context_ = nullptr;
612
613   PMPrintSession print_session =
614       static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
615   PMSessionEndPageNoDialog(print_session);
616 }
617
618 void PrintingContextMac::ReleaseContext() {
619   print_info_.reset();
620   context_ = nullptr;
621 }
622
623 printing::NativeDrawingContext PrintingContextMac::context() const {
624   return context_;
625 }
626
627 }  // namespace printing