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.
5 #include "printing/printing_context_mac.h"
7 #import <AppKit/AppKit.h>
8 #include <CoreFoundation/CoreFoundation.h>
9 #import <QuartzCore/QuartzCore.h>
10 #include <cups/cups.h>
15 #include "base/apple/bridging.h"
16 #include "base/apple/foundation_util.h"
17 #include "base/apple/osstatus_logging.h"
18 #include "base/apple/scoped_cftyperef.h"
19 #include "base/apple/scoped_typeref.h"
20 #include "base/check_op.h"
21 #include "base/strings/string_piece.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/values.h"
25 #include "build/build_config.h"
26 #include "printing/buildflags/buildflags.h"
27 #include "printing/metafile.h"
28 #include "printing/mojom/print.mojom.h"
29 #include "printing/print_job_constants_cups.h"
30 #include "printing/print_settings_initializer_mac.h"
31 #include "printing/printing_features.h"
32 #include "printing/units.h"
34 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
35 #include "base/numerics/safe_conversions.h"
36 #include "base/types/expected.h"
44 struct ScopedPMTypeTraits {
45 static T InvalidValue() { return nullptr; }
46 static T Retain(T object) {
50 static void Release(T object) { PMRelease(object); }
54 using ScopedPMType = base::apple::ScopedTypeRef<T, ScopedPMTypeTraits<T>>;
56 const int kMaxPaperSizeDifferenceInPoints = 2;
58 // Return true if PPD name of paper is equal.
59 bool IsPaperNameEqual(CFStringRef name1, const PMPaper& paper2) {
60 CFStringRef name2 = nullptr;
61 return (name1 && PMPaperGetPPDPaperName(paper2, &name2) == noErr) &&
62 (CFStringCompare(name1, name2, kCFCompareCaseInsensitive) ==
66 PMPaper MatchPaper(CFArrayRef paper_list,
70 double best_match = std::numeric_limits<double>::max();
71 PMPaper best_matching_paper = nullptr;
73 CFIndex num_papers = CFArrayGetCount(paper_list);
74 for (CFIndex i = 0; i < num_papers; ++i) {
75 PMPaper paper = (PMPaper)CFArrayGetValueAtIndex(paper_list, i);
76 double paper_width = 0.0;
77 double paper_height = 0.0;
78 PMPaperGetWidth(paper, &paper_width);
79 PMPaperGetHeight(paper, &paper_height);
81 std::max(fabs(width - paper_width), fabs(height - paper_height));
83 // Ignore papers with size too different from expected.
84 if (difference > kMaxPaperSizeDifferenceInPoints) {
88 if (name && IsPaperNameEqual(name, paper))
91 if (difference < best_match) {
92 best_matching_paper = paper;
93 best_match = difference;
96 return best_matching_paper;
99 bool IsIppColorModelColorful(mojom::ColorModel color_model) {
100 // Accept `kUnknownColorModel` as it can occur with raw CUPS printers.
101 // Treat it similarly to the behavior in `GetColorModelForModel()`.
102 if (color_model == mojom::ColorModel::kUnknownColorModel) {
105 return IsColorModelSelected(color_model).value();
108 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
110 // The set of "capture" routines run in the browser process.
111 // The set of "apply" routines run in the Print Backend service.
113 base::expected<std::vector<uint8_t>, mojom::ResultCode>
114 CaptureSystemPrintSettings(PMPrintSettings& print_settings) {
115 CFDataRef data_ref = nullptr;
116 OSStatus status = PMPrintSettingsCreateDataRepresentation(
117 print_settings, &data_ref, kPMDataFormatXMLDefault);
118 if (status != noErr) {
119 OSSTATUS_LOG(ERROR, status)
120 << "Failed to create data representation of print settings";
121 return base::unexpected(mojom::ResultCode::kFailed);
124 base::apple::ScopedCFTypeRef<CFDataRef> scoped_data_ref(data_ref);
125 uint32_t data_size = CFDataGetLength(data_ref);
126 std::vector<uint8_t> capture_data(data_size);
127 CFDataGetBytes(data_ref, CFRangeMake(0, data_size),
128 static_cast<UInt8*>(&capture_data.front()));
132 base::expected<std::vector<uint8_t>, mojom::ResultCode> CaptureSystemPageFormat(
133 PMPageFormat& page_format) {
134 CFDataRef data_ref = nullptr;
135 OSStatus status = PMPageFormatCreateDataRepresentation(
136 page_format, &data_ref, kPMDataFormatXMLDefault);
137 if (status != noErr) {
138 OSSTATUS_LOG(ERROR, status)
139 << "Failed to create data representation of page format";
140 return base::unexpected(mojom::ResultCode::kFailed);
143 uint32_t data_size = CFDataGetLength(data_ref);
144 std::vector<uint8_t> capture_data(data_size);
145 CFDataGetBytes(data_ref, CFRangeMake(0, data_size),
146 static_cast<UInt8*>(&capture_data.front()));
150 base::expected<base::apple::ScopedCFTypeRef<CFStringRef>, mojom::ResultCode>
151 CaptureSystemDestinationFormat(PMPrintSession& print_session,
152 PMPrintSettings& print_settings) {
153 CFStringRef destination_format_ref = nullptr;
154 OSStatus status = PMSessionCopyDestinationFormat(
155 print_session, print_settings, &destination_format_ref);
156 if (status != noErr) {
157 OSSTATUS_LOG(ERROR, status) << "Failed to get printing destination format";
158 return base::unexpected(mojom::ResultCode::kFailed);
160 return base::apple::ScopedCFTypeRef<CFStringRef>(destination_format_ref);
163 base::expected<base::apple::ScopedCFTypeRef<CFURLRef>, mojom::ResultCode>
164 CaptureSystemDestinationLocation(PMPrintSession& print_session,
165 PMPrintSettings& print_settings) {
166 CFURLRef destination_location_ref = nullptr;
167 OSStatus status = PMSessionCopyDestinationLocation(
168 print_session, print_settings, &destination_location_ref);
169 if (status != noErr) {
170 OSSTATUS_LOG(ERROR, status)
171 << "Failed to get printing destination location";
172 return base::unexpected(mojom::ResultCode::kFailed);
174 return base::apple::ScopedCFTypeRef<CFURLRef>(destination_location_ref);
177 mojom::ResultCode CaptureSystemPrintDialogData(NSPrintInfo* print_info,
178 PrintSettings* settings) {
179 PMPrintSettings print_settings =
180 (PMPrintSettings)[print_info PMPrintSettings];
182 base::expected<std::vector<uint8_t>, mojom::ResultCode> print_settings_data =
183 CaptureSystemPrintSettings(print_settings);
184 if (!print_settings_data.has_value()) {
185 return print_settings_data.error();
188 PMPageFormat page_format =
189 static_cast<PMPageFormat>([print_info PMPageFormat]);
191 base::expected<std::vector<uint8_t>, mojom::ResultCode> page_format_data =
192 CaptureSystemPageFormat(page_format);
193 if (!page_format_data.has_value()) {
194 return page_format_data.error();
197 PMPrintSession print_session =
198 static_cast<PMPrintSession>([print_info PMPrintSession]);
200 PMDestinationType destination_type = kPMDestinationInvalid;
201 PMSessionGetDestinationType(print_session, print_settings, &destination_type);
203 base::expected<base::apple::ScopedCFTypeRef<CFStringRef>, mojom::ResultCode>
205 CaptureSystemDestinationFormat(print_session, print_settings);
206 if (!destination_format.has_value()) {
207 return destination_format.error();
210 base::expected<base::apple::ScopedCFTypeRef<CFURLRef>, mojom::ResultCode>
211 destination_location =
212 CaptureSystemDestinationLocation(print_session, print_settings);
213 if (!destination_location.has_value()) {
214 return destination_location.error();
217 base::Value::Dict dialog_data;
218 dialog_data.Set(kMacSystemPrintDialogDataPrintSettings,
219 std::move(print_settings_data.value()));
220 dialog_data.Set(kMacSystemPrintDialogDataPageFormat,
221 std::move(page_format_data.value()));
222 dialog_data.Set(kMacSystemPrintDialogDataDestinationType, destination_type);
223 if (destination_format.value()) {
225 kMacSystemPrintDialogDataDestinationFormat,
226 base::SysCFStringRefToUTF8(destination_format.value().get()));
228 if (destination_location.value()) {
229 dialog_data.Set(kMacSystemPrintDialogDataDestinationLocation,
230 base::SysCFStringRefToUTF8(
231 CFURLGetString(destination_location.value().get())));
233 settings->set_system_print_dialog_data(std::move(dialog_data));
234 return mojom::ResultCode::kSuccess;
237 void ApplySystemPrintSettings(const base::Value::Dict& system_print_dialog_data,
238 NSPrintInfo* print_info,
239 PMPrintSession& print_session,
240 PMPrintSettings& print_settings) {
241 const base::Value::BlobStorage* data =
242 system_print_dialog_data.FindBlob(kMacSystemPrintDialogDataPrintSettings);
244 uint32_t data_size = data->size();
245 CHECK_GT(data_size, 0u);
247 CFDataCreate(kCFAllocatorDefault,
248 static_cast<const UInt8*>(&data->front()), data_size);
250 base::apple::ScopedCFTypeRef<CFDataRef> scoped_data_ref(data_ref);
252 ScopedPMType<PMPrintSettings> new_print_settings;
253 OSStatus status = PMPrintSettingsCreateWithDataRepresentation(
254 data_ref, new_print_settings.InitializeInto());
255 CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
257 status = PMSessionValidatePrintSettings(
258 print_session, new_print_settings.get(), kPMDontWantBoolean);
259 CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
260 status = PMCopyPrintSettings(new_print_settings.get(), print_settings);
261 CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
263 [print_info updateFromPMPrintSettings];
266 void ApplySystemPageFormat(const base::Value::Dict& system_print_dialog_data,
267 NSPrintInfo* print_info,
268 PMPrintSession& print_session,
269 PMPageFormat& page_format) {
270 const base::Value::BlobStorage* data =
271 system_print_dialog_data.FindBlob(kMacSystemPrintDialogDataPageFormat);
273 uint32_t data_size = data->size();
274 CHECK_GT(data_size, 0u);
276 CFDataCreate(kCFAllocatorDefault,
277 static_cast<const UInt8*>(&data->front()), data_size);
280 ScopedPMType<PMPageFormat> new_page_format;
281 OSStatus status = PMPageFormatCreateWithDataRepresentation(
282 data_ref, new_page_format.InitializeInto());
283 CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
284 status = PMSessionValidatePageFormat(print_session, page_format,
286 status = PMCopyPageFormat(new_page_format.get(), page_format);
287 CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
289 [print_info updateFromPMPageFormat];
292 void ApplySystemDestination(const std::u16string& device_name,
293 const base::Value::Dict& system_print_dialog_data,
294 PMPrintSession& print_session,
295 PMPrintSettings& print_settings) {
296 absl::optional<int> destination_type = system_print_dialog_data.FindInt(
297 kMacSystemPrintDialogDataDestinationType);
299 CHECK(destination_type.has_value());
300 CHECK(base::IsValueInRangeForNumericType<uint16_t>(*destination_type));
302 const std::string* destination_format_str =
303 system_print_dialog_data.FindString(
304 kMacSystemPrintDialogDataDestinationFormat);
305 const std::string* destination_location_str =
306 system_print_dialog_data.FindString(
307 kMacSystemPrintDialogDataDestinationLocation);
309 base::apple::ScopedCFTypeRef<CFStringRef> destination_format;
310 if (destination_format_str) {
311 destination_format.reset(
312 base::SysUTF8ToCFStringRef(*destination_format_str));
315 base::apple::ScopedCFTypeRef<CFURLRef> destination_location;
316 if (destination_location_str) {
317 destination_location.reset(CFURLCreateWithFileSystemPath(
319 base::SysUTF8ToCFStringRef(*destination_location_str).get(),
320 kCFURLPOSIXPathStyle,
321 /*isDirectory=*/FALSE));
324 base::apple::ScopedCFTypeRef<CFStringRef> destination_name(
325 base::SysUTF16ToCFStringRef(device_name));
326 ScopedPMType<PMPrinter> printer(
327 PMPrinterCreateFromPrinterID(destination_name.get()));
329 OSStatus status = PMSessionSetCurrentPMPrinter(print_session, printer.get());
330 CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
332 status = PMSessionSetDestination(
333 print_session, print_settings,
334 static_cast<PMDestinationType>(*destination_type),
335 destination_format.get(), destination_location.get());
336 CHECK_EQ(status, noErr) << logging::DescriptionFromOSStatus(status);
339 void ApplySystemPrintDialogData(
340 const std::u16string& device_name,
341 const base::Value::Dict& system_print_dialog_data,
342 NSPrintInfo* print_info) {
343 PMPrintSession print_session =
344 static_cast<PMPrintSession>([print_info PMPrintSession]);
345 PMPrintSettings print_settings =
346 static_cast<PMPrintSettings>([print_info PMPrintSettings]);
347 PMPageFormat page_format =
348 static_cast<PMPageFormat>([print_info PMPageFormat]);
350 ApplySystemDestination(device_name, system_print_dialog_data, print_session,
352 ApplySystemPrintSettings(system_print_dialog_data, print_info, print_session,
354 ApplySystemPageFormat(system_print_dialog_data, print_info, print_session,
357 #endif // BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
362 std::unique_ptr<PrintingContext> PrintingContext::CreateImpl(
364 bool skip_system_calls) {
365 auto context = std::make_unique<PrintingContextMac>(delegate);
366 #if BUILDFLAG(ENABLE_OOP_PRINTING)
367 if (skip_system_calls)
368 context->set_skip_system_calls();
373 PrintingContextMac::PrintingContextMac(Delegate* delegate)
374 : PrintingContext(delegate),
375 print_info_([NSPrintInfo.sharedPrintInfo copy]) {}
377 PrintingContextMac::~PrintingContextMac() {
381 void PrintingContextMac::AskUserForSettings(int max_pages,
384 PrintSettingsCallback callback) {
385 // Exceptions can also happen when the NSPrintPanel is being
386 // deallocated, so it must be autoreleased within this scope.
388 DCHECK(NSThread.isMainThread);
390 // We deliberately don't feed max_pages into the dialog, because setting
391 // NSPrintLastPage makes the print dialog pre-select the option to only
394 // TODO(stuartmorgan): implement 'print selection only' (probably requires
395 // adding a new custom view to the panel on 10.5; 10.6 has
396 // NSPrintPanelShowsPrintSelection).
397 NSPrintPanel* panel = [NSPrintPanel printPanel];
398 panel.options |= NSPrintPanelShowsPaperSize | NSPrintPanelShowsOrientation |
399 NSPrintPanelShowsScaling;
401 // Set the print job title text.
402 gfx::NativeView parent_view = delegate_->GetParentView();
404 NSString* job_title = parent_view.GetNativeNSView().window.title;
406 PMPrintSettings print_settings =
407 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
408 PMPrintSettingsSetJobName(print_settings,
409 base::apple::NSToCFPtrCast(job_title));
410 [print_info_ updateFromPMPrintSettings];
414 // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
415 // Will require restructuring the PrintingContext API to use a callback.
417 // This function may be called in the middle of a CATransaction, where
418 // running a modal panel is forbidden. That situation isn't ideal, but from
419 // this code's POV the right answer is to defer running the panel until
420 // after the current transaction. See https://crbug.com/849538.
421 __block auto block_callback = std::move(callback);
422 [CATransaction setCompletionBlock:^{
423 NSInteger selection = [panel runModalWithPrintInfo:print_info_];
424 if (selection == NSModalResponseOK) {
425 print_info_ = [panel printInfo];
426 settings_->set_ranges(GetPageRangesFromPrintInfo());
427 InitPrintSettingsFromPrintInfo();
428 mojom::ResultCode result = mojom::ResultCode::kSuccess;
429 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
430 if (features::ShouldPrintJobOop()) {
431 result = CaptureSystemPrintDialogData(print_info_, settings_.get());
434 std::move(block_callback).Run(result);
436 std::move(block_callback).Run(mojom::ResultCode::kCanceled);
442 gfx::Size PrintingContextMac::GetPdfPaperSizeDeviceUnits() {
443 // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
444 // with a clean slate.
445 print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
446 UpdatePageFormatWithPaperInfo();
448 PMPageFormat page_format =
449 static_cast<PMPageFormat>([print_info_ PMPageFormat]);
451 PMGetAdjustedPaperRect(page_format, &paper_rect);
453 // Device units are in points. Units per inch is 72.
454 gfx::Size physical_size_device_units((paper_rect.right - paper_rect.left),
455 (paper_rect.bottom - paper_rect.top));
456 DCHECK(settings_->device_units_per_inch() == kPointsPerInch);
457 return physical_size_device_units;
460 mojom::ResultCode PrintingContextMac::UseDefaultSettings() {
461 DCHECK(!in_print_job_);
463 print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
464 settings_->set_ranges(GetPageRangesFromPrintInfo());
465 InitPrintSettingsFromPrintInfo();
467 return mojom::ResultCode::kSuccess;
470 mojom::ResultCode PrintingContextMac::UpdatePrinterSettings(
471 const PrinterSettings& printer_settings) {
472 DCHECK(!printer_settings.show_system_dialog);
473 DCHECK(!in_print_job_);
475 // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
476 // with a clean slate.
477 print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
479 if (printer_settings.external_preview) {
480 if (!SetPrintPreviewJob())
483 // Don't need this for preview.
484 if (!SetPrinter(base::UTF16ToUTF8(settings_->device_name())) ||
485 !SetCopiesInPrintSettings(settings_->copies()) ||
486 !SetCollateInPrintSettings(settings_->collate()) ||
487 !SetDuplexModeInPrintSettings(settings_->duplex_mode()) ||
488 !SetOutputColor(static_cast<int>(settings_->color())) ||
489 !SetResolution(settings_->dpi_size())) {
494 if (!UpdatePageFormatWithPaperInfo() ||
495 !SetOrientationIsLandscape(settings_->landscape())) {
499 [print_info_ updateFromPMPrintSettings];
501 InitPrintSettingsFromPrintInfo();
502 return mojom::ResultCode::kSuccess;
505 bool PrintingContextMac::SetPrintPreviewJob() {
506 PMPrintSession print_session =
507 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
508 PMPrintSettings print_settings =
509 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
510 return PMSessionSetDestination(print_session, print_settings,
511 kPMDestinationPreview, nullptr,
515 void PrintingContextMac::InitPrintSettingsFromPrintInfo() {
516 PMPrintSession print_session =
517 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
518 PMPageFormat page_format =
519 static_cast<PMPageFormat>([print_info_ PMPageFormat]);
521 PMSessionGetCurrentPrinter(print_session, &printer);
522 PrintSettingsInitializerMac::InitPrintSettings(printer, page_format,
526 bool PrintingContextMac::SetPrinter(const std::string& device_name) {
528 PMPrintSession print_session =
529 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
531 PMPrinter current_printer;
532 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
535 CFStringRef current_printer_id = PMPrinterGetID(current_printer);
536 if (!current_printer_id)
539 base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
540 base::SysUTF8ToCFStringRef(device_name));
541 if (!new_printer_id.get())
544 if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
549 ScopedPMType<PMPrinter> new_printer(
550 PMPrinterCreateFromPrinterID(new_printer_id.get()));
555 return PMSessionSetCurrentPMPrinter(print_session, new_printer.get()) ==
559 bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
560 PMPrintSession print_session =
561 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
563 PMPageFormat default_page_format =
564 static_cast<PMPageFormat>([print_info_ PMPageFormat]);
566 PMPrinter current_printer = nullptr;
567 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
570 double page_width = 0.0;
571 double page_height = 0.0;
572 base::apple::ScopedCFTypeRef<CFStringRef> paper_name;
573 PMPaperMargins margins = {0};
575 const PrintSettings::RequestedMedia& media = settings_->requested_media();
576 if (media.IsDefault()) {
577 PMPaper default_paper;
578 if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr ||
579 PMPaperGetWidth(default_paper, &page_width) != noErr ||
580 PMPaperGetHeight(default_paper, &page_height) != noErr) {
584 // Ignore result, because we can continue without following.
585 CFStringRef tmp_paper_name = nullptr;
586 PMPaperGetPPDPaperName(default_paper, &tmp_paper_name);
587 PMPaperGetMargins(default_paper, &margins);
588 paper_name.reset(tmp_paper_name, base::scoped_policy::RETAIN);
590 const double kMultiplier =
591 kPointsPerInch / static_cast<float>(kMicronsPerInch);
592 page_width = media.size_microns.width() * kMultiplier;
593 page_height = media.size_microns.height() * kMultiplier;
594 paper_name.reset(base::SysUTF8ToCFStringRef(media.vendor_id));
597 CFArrayRef paper_list = nullptr;
598 if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
601 PMPaper best_matching_paper =
602 MatchPaper(paper_list, paper_name.get(), page_width, page_height);
604 if (best_matching_paper)
605 return UpdatePageFormatWithPaper(best_matching_paper, default_page_format);
607 // Do nothing if unmatched paper was default system paper.
608 if (media.IsDefault())
611 ScopedPMType<PMPaper> paper;
612 if (PMPaperCreateCustom(current_printer, CFSTR("Custom paper ID"),
613 CFSTR("Custom paper"), page_width, page_height,
614 &margins, paper.InitializeInto()) != noErr) {
617 return UpdatePageFormatWithPaper(paper.get(), default_page_format);
620 bool PrintingContextMac::UpdatePageFormatWithPaper(PMPaper paper,
621 PMPageFormat page_format) {
622 ScopedPMType<PMPageFormat> new_format;
623 if (PMCreatePageFormatWithPMPaper(new_format.InitializeInto(), paper) !=
627 // Copy over the original format with the new page format.
628 bool result = (PMCopyPageFormat(new_format.get(), page_format) == noErr);
629 [print_info_ updateFromPMPageFormat];
633 bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
637 PMPrintSettings print_settings =
638 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
639 return PMSetCopies(print_settings, copies, false) == noErr;
642 bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
643 PMPrintSettings print_settings =
644 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
645 return PMSetCollate(print_settings, collate) == noErr;
648 bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
649 PMPageFormat page_format =
650 static_cast<PMPageFormat>([print_info_ PMPageFormat]);
652 PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
654 if (PMSetOrientation(page_format, orientation, false) != noErr)
657 PMPrintSession print_session =
658 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
660 PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
662 [print_info_ updateFromPMPageFormat];
666 bool PrintingContextMac::SetDuplexModeInPrintSettings(mojom::DuplexMode mode) {
667 PMDuplexMode duplexSetting;
669 case mojom::DuplexMode::kLongEdge:
670 duplexSetting = kPMDuplexNoTumble;
672 case mojom::DuplexMode::kShortEdge:
673 duplexSetting = kPMDuplexTumble;
675 case mojom::DuplexMode::kSimplex:
676 duplexSetting = kPMDuplexNone;
678 default: // kUnknownDuplexMode
682 PMPrintSettings print_settings =
683 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
684 return PMSetDuplex(print_settings, duplexSetting) == noErr;
687 bool PrintingContextMac::SetOutputColor(int color_mode) {
688 const mojom::ColorModel color_model = ColorModeToColorModel(color_mode);
690 if (!base::FeatureList::IsEnabled(features::kCupsIppPrintingBackend)) {
691 std::string color_setting_name;
692 std::string color_value;
693 GetColorModelForModel(color_model, &color_setting_name, &color_value);
694 return SetKeyValue(color_setting_name, color_value);
697 // First, set the default CUPS IPP output color.
698 if (!SetKeyValue(CUPS_PRINT_COLOR_MODE,
699 GetIppColorModelForModel(color_model))) {
703 // Even when interfacing with printer settings using CUPS IPP, the print job
704 // may still expect PPD color values if the printer was added to the system
705 // with a PPD. To avoid parsing PPDs (which is the point of using CUPS IPP),
706 // set every single known PPD color setting and hope that one of them sticks.
707 const bool is_color = IsIppColorModelColorful(color_model);
708 for (const auto& setting : GetKnownPpdColorSettings()) {
709 const base::StringPiece& color_setting_name = setting.name;
710 const base::StringPiece& color_value =
711 is_color ? setting.color : setting.bw;
712 if (!SetKeyValue(color_setting_name, color_value))
719 bool PrintingContextMac::SetResolution(const gfx::Size& dpi_size) {
720 if (dpi_size.IsEmpty())
723 PMPrintSession print_session =
724 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
725 PMPrinter current_printer;
726 if (PMSessionGetCurrentPrinter(print_session, ¤t_printer) != noErr)
729 PMResolution resolution;
730 resolution.hRes = dpi_size.width();
731 resolution.vRes = dpi_size.height();
733 PMPrintSettings print_settings =
734 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
735 return PMPrinterSetOutputResolution(current_printer, print_settings,
736 &resolution) == noErr;
739 bool PrintingContextMac::SetKeyValue(base::StringPiece key,
740 base::StringPiece value) {
741 PMPrintSettings print_settings =
742 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
743 base::apple::ScopedCFTypeRef<CFStringRef> cf_key =
744 base::SysUTF8ToCFStringRef(key);
745 base::apple::ScopedCFTypeRef<CFStringRef> cf_value =
746 base::SysUTF8ToCFStringRef(value);
748 return PMPrintSettingsSetValue(print_settings, cf_key.get(), cf_value.get(),
749 /*locked=*/false) == noErr;
752 PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
753 PageRanges page_ranges;
754 NSDictionary* print_info_dict = [print_info_ dictionary];
755 if (![print_info_dict[NSPrintAllPages] boolValue]) {
757 range.from = [print_info_dict[NSPrintFirstPage] intValue] - 1;
758 range.to = [print_info_dict[NSPrintLastPage] intValue] - 1;
759 page_ranges.push_back(range);
764 mojom::ResultCode PrintingContextMac::NewDocument(
765 const std::u16string& document_name) {
766 DCHECK(!in_print_job_);
768 in_print_job_ = true;
770 if (skip_system_calls())
771 return mojom::ResultCode::kSuccess;
773 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
774 if (features::ShouldPrintJobOop() &&
775 !settings_->system_print_dialog_data().empty()) {
776 // NOTE: Reset `print_info_` with a copy of `sharedPrintInfo` so as to
777 // start with a clean slate.
778 print_info_ = [[NSPrintInfo sharedPrintInfo] copy];
780 ApplySystemPrintDialogData(settings_->device_name(),
781 settings_->system_print_dialog_data(),
786 PMPrintSession print_session =
787 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
788 PMPrintSettings print_settings =
789 static_cast<PMPrintSettings>([print_info_ PMPrintSettings]);
790 PMPageFormat page_format =
791 static_cast<PMPageFormat>([print_info_ PMPageFormat]);
793 base::apple::ScopedCFTypeRef<CFStringRef> job_title =
794 base::SysUTF16ToCFStringRef(document_name);
795 PMPrintSettingsSetJobName(print_settings, job_title.get());
797 OSStatus status = PMSessionBeginCGDocumentNoDialog(
798 print_session, print_settings, page_format);
802 return mojom::ResultCode::kSuccess;
805 mojom::ResultCode PrintingContextMac::NewPage() {
807 return mojom::ResultCode::kCanceled;
808 DCHECK(in_print_job_);
811 PMPrintSession print_session =
812 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
813 PMPageFormat page_format =
814 static_cast<PMPageFormat>([print_info_ PMPageFormat]);
816 status = PMSessionBeginPageNoDialog(print_session, page_format, nullptr);
819 status = PMSessionGetCGGraphicsContext(print_session, &context_);
823 return mojom::ResultCode::kSuccess;
826 mojom::ResultCode PrintingContextMac::PageDone() {
828 return mojom::ResultCode::kCanceled;
829 DCHECK(in_print_job_);
832 PMPrintSession print_session =
833 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
834 OSStatus status = PMSessionEndPageNoDialog(print_session);
839 return mojom::ResultCode::kSuccess;
842 mojom::ResultCode PrintingContextMac::PrintDocument(
843 const MetafilePlayer& metafile,
844 const PrintSettings& settings,
845 uint32_t num_pages) {
846 const PageSetup& page_setup = settings.page_setup_device_units();
847 const CGRect paper_rect = gfx::Rect(page_setup.physical_size()).ToCGRect();
849 for (size_t metafile_page_number = 1; metafile_page_number <= num_pages;
850 metafile_page_number++) {
851 mojom::ResultCode result = NewPage();
852 if (result != mojom::ResultCode::kSuccess)
854 if (!metafile.RenderPage(metafile_page_number, context_, paper_rect,
855 /*autorotate=*/true, /*fit_to_page=*/false)) {
856 return mojom::ResultCode::kFailed;
859 if (result != mojom::ResultCode::kSuccess)
862 return mojom::ResultCode::kSuccess;
865 mojom::ResultCode PrintingContextMac::DocumentDone() {
867 return mojom::ResultCode::kCanceled;
868 DCHECK(in_print_job_);
870 PMPrintSession print_session =
871 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
872 OSStatus status = PMSessionEndDocumentNoDialog(print_session);
877 return mojom::ResultCode::kSuccess;
880 void PrintingContextMac::Cancel() {
881 abort_printing_ = true;
882 in_print_job_ = false;
885 PMPrintSession print_session =
886 static_cast<PMPrintSession>([print_info_ PMPrintSession]);
887 PMSessionEndPageNoDialog(print_session);
890 void PrintingContextMac::ReleaseContext() {
895 printing::NativeDrawingContext PrintingContextMac::context() const {
899 } // namespace printing