1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsPrintSettingsX.h"
7 #include "nsObjCExceptions.h"
11 #include "nsCocoaUtils.h"
12 #include "nsXULAppAPI.h"
14 #include "mozilla/Preferences.h"
15 #include "mozilla/StaticPrefs_print.h"
16 #include "nsPrinterCUPS.h"
18 using namespace mozilla;
20 NS_IMPL_ISUPPORTS_INHERITED(nsPrintSettingsX, nsPrintSettings, nsPrintSettingsX)
22 nsPrintSettingsX::nsPrintSettingsX() {
23 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
25 mDestination = kPMDestinationInvalid;
27 NS_OBJC_END_TRY_IGNORE_BLOCK;
30 already_AddRefed<nsIPrintSettings> CreatePlatformPrintSettings(
31 const PrintSettingsInitializer& aSettings) {
32 RefPtr<nsPrintSettings> settings = new nsPrintSettingsX();
33 settings->InitWithInitializer(aSettings);
34 settings->SetDefaultFileName();
35 return settings.forget();
38 nsPrintSettingsX& nsPrintSettingsX::operator=(const nsPrintSettingsX& rhs) {
43 nsPrintSettings::operator=(rhs);
45 mDestination = rhs.mDestination;
46 mDisposition = rhs.mDisposition;
48 // We don't copy mSystemPrintInfo here, so any copied printSettings will start
49 // out without a wrapped printInfo, just using our internal settings. The
50 // system printInfo is used *only* by the nsPrintSettingsX to which it was
51 // originally passed (when the user ran a system print UI dialog).
56 nsresult nsPrintSettingsX::_Clone(nsIPrintSettings** _retval) {
57 NS_ENSURE_ARG_POINTER(_retval);
58 auto newSettings = MakeRefPtr<nsPrintSettingsX>();
60 newSettings.forget(_retval);
64 NS_IMETHODIMP nsPrintSettingsX::_Assign(nsIPrintSettings* aPS) {
65 nsPrintSettingsX* printSettingsX = static_cast<nsPrintSettingsX*>(aPS);
66 if (!printSettingsX) {
67 return NS_ERROR_UNEXPECTED;
69 *this = *printSettingsX;
73 struct KnownMonochromeSetting {
74 const NSString* mName;
75 const NSString* mValue;
78 #define DECLARE_KNOWN_MONOCHROME_SETTING(key_, value_) \
81 static const KnownMonochromeSetting kKnownMonochromeSettings[] = {
82 CUPS_EACH_MONOCHROME_PRINTER_SETTING(DECLARE_KNOWN_MONOCHROME_SETTING)};
83 #undef DECLARE_KNOWN_MONOCHROME_SETTING
85 NSPrintInfo* nsPrintSettingsX::CreateOrCopyPrintInfo(bool aWithScaling) {
86 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
88 // If we have a printInfo that came from the system print UI, use it so that
89 // any printer-specific settings we don't know about will still be used.
90 if (mSystemPrintInfo) {
91 NSPrintInfo* sysPrintInfo = [mSystemPrintInfo copy];
92 // Any required scaling will be done by Gecko, so we don't want it here.
93 [sysPrintInfo setScalingFactor:1.0f];
97 // Note that the app shared `sharedPrintInfo` object is special! The system
98 // print dialog and print settings dialog update it with the values chosen
99 // by the user. Using that object here to initialize new nsPrintSettingsX
100 // objects could mask bugs in our code where we fail to save and/or restore
101 // print settings ourselves (e.g., bug 1636725). On other platforms we only
102 // initialize new nsPrintSettings objects from the settings that we save to
103 // prefs. Perhaps we should stop using sharedPrintInfo here for consistency?
104 NSPrintInfo* printInfo = [[NSPrintInfo sharedPrintInfo] copy];
107 if (GetSheetOrientation() == kPortraitOrientation) {
108 paperSize.width = CocoaPointsFromPaperSize(mPaperWidth);
109 paperSize.height = CocoaPointsFromPaperSize(mPaperHeight);
111 paperSize.width = CocoaPointsFromPaperSize(mPaperHeight);
112 paperSize.height = CocoaPointsFromPaperSize(mPaperWidth);
114 [printInfo setPaperSize:paperSize];
116 if (paperSize.width > paperSize.height) {
117 [printInfo setOrientation:NSPaperOrientationLandscape];
119 [printInfo setOrientation:NSPaperOrientationPortrait];
122 [printInfo setTopMargin:mUnwriteableMargin.top];
123 [printInfo setRightMargin:mUnwriteableMargin.right];
124 [printInfo setBottomMargin:mUnwriteableMargin.bottom];
125 [printInfo setLeftMargin:mUnwriteableMargin.left];
127 // If the printer name is the name of our pseudo print-to-PDF printer, the
128 // following `setPrinter` call will fail silently, since macOS doesn't know
129 // anything about it. That's OK, because mPrinter is our canonical source of
131 // Actually, it seems Mac OS X 10.12 (the oldest version of Mac that we
132 // support) hangs if the printer name is not recognized. For now we explicitly
133 // check for our print-to-PDF printer, but that is not ideal since we should
134 // really localize the name of this printer at some point. Once we drop
135 // support for 10.12 we should remove this check.
136 if (!mPrinter.EqualsLiteral("Mozilla Save to PDF")) {
137 [printInfo setPrinter:[NSPrinter printerWithName:nsCocoaUtils::ToNSString(
141 // Scaling is handled by gecko, we do NOT want the cocoa printing system to
142 // add a second scaling on top of that. So we only set the true scaling factor
143 // here if the caller explicitly asked for it.
144 [printInfo setScalingFactor:CGFloat(aWithScaling ? mScaling : 1.0f)];
146 const bool allPages = mPageRanges.IsEmpty();
148 NSMutableDictionary* dict = [printInfo dictionary];
149 [dict setObject:[NSNumber numberWithInt:mNumCopies] forKey:NSPrintCopies];
150 [dict setObject:[NSNumber numberWithBool:allPages] forKey:NSPrintAllPages];
154 for (size_t i = 0; i < mPageRanges.Length(); i += 2) {
155 start = std::min(start, mPageRanges[i]);
156 end = std::max(end, mPageRanges[i + 1]);
159 [dict setObject:[NSNumber numberWithInt:start] forKey:NSPrintFirstPage];
160 [dict setObject:[NSNumber numberWithInt:end] forKey:NSPrintLastPage];
162 NSURL* jobSavingURL = nullptr;
163 if (!mToFileName.IsEmpty()) {
165 [NSURL fileURLWithPath:nsCocoaUtils::ToNSString(mToFileName)];
167 // Note: the PMPrintSettingsSetJobName call in nsPrintDialogServiceX::Show
168 // seems to mean that we get a sensible file name pre-populated in the
169 // dialog there, although our mToFileName is expected to be a full path,
170 // and it's less clear where the rest of the path (the directory to save
171 // to) in nsPrintDialogServiceX::Show comes from (perhaps from the use
172 // of `sharedPrintInfo` to initialize new nsPrintSettingsX objects).
173 [dict setObject:jobSavingURL forKey:NSPrintJobSavingURL];
177 if (mDisposition.IsEmpty()) {
178 // NOTE: It's unclear what to do for kOutputDestinationStream but this is
179 // only for the native print dialog where that can't happen.
180 if (mOutputDestination == kOutputDestinationFile) {
181 [printInfo setJobDisposition:NSPrintSaveJob];
183 [printInfo setJobDisposition:NSPrintSpoolJob];
186 [printInfo setJobDisposition:nsCocoaUtils::ToNSString(mDisposition)];
189 PMDuplexMode duplexSetting;
192 // This can't happen :) but if it does, we treat it as "none".
193 MOZ_FALLTHROUGH_ASSERT("Unknown duplex value");
195 duplexSetting = kPMDuplexNone;
197 case kDuplexFlipOnLongEdge:
198 duplexSetting = kPMDuplexNoTumble;
200 case kDuplexFlipOnShortEdge:
201 duplexSetting = kPMDuplexTumble;
205 NSMutableDictionary* printSettings = [printInfo printSettings];
206 [printSettings setObject:[NSNumber numberWithUnsignedShort:duplexSetting]
207 forKey:@"com_apple_print_PrintSettings_PMDuplexing"];
209 if (mDestination != kPMDestinationInvalid) {
210 // Required to support PDF-workflow destinations such as Save to Web
213 setObject:[NSNumber numberWithUnsignedShort:mDestination]
214 forKey:@"com_apple_print_PrintSettings_PMDestinationType"];
217 setObject:[jobSavingURL absoluteString]
218 forKey:@"com_apple_print_PrintSettings_PMOutputFilename"];
222 if (StaticPrefs::print_cups_monochrome_enabled() && !GetPrintInColor()) {
223 for (const auto& setting : kKnownMonochromeSettings) {
224 [printSettings setObject:setting.mValue forKey:setting.mName];
226 auto applySetting = [&](const nsACString& aKey, const nsACString& aValue) {
227 [printSettings setObject:nsCocoaUtils::ToNSString(aValue)
228 forKey:nsCocoaUtils::ToNSString(aKey)];
230 nsPrinterCUPS::ForEachExtraMonochromeSetting(applySetting);
235 NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
238 void nsPrintSettingsX::SetFromPrintInfo(NSPrintInfo* aPrintInfo,
239 bool aAdoptPrintInfo) {
240 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
242 // Set page-size/margins.
243 NSSize paperSize = [aPrintInfo paperSize];
244 const bool areSheetsOfPaperPortraitMode =
245 ([aPrintInfo orientation] == NSPaperOrientationPortrait);
247 // If our MacOS print settings say that we're producing portrait-mode sheets
248 // of paper, then our page format must also be portrait-mode; unless we've
249 // got a pages-per-sheet value with orthogonal pages/sheets, in which case
251 const bool arePagesPortraitMode =
252 (areSheetsOfPaperPortraitMode != HasOrthogonalPagesPerSheet());
254 if (arePagesPortraitMode) {
255 mOrientation = nsIPrintSettings::kPortraitOrientation;
256 SetPaperWidth(PaperSizeFromCocoaPoints(paperSize.width));
257 SetPaperHeight(PaperSizeFromCocoaPoints(paperSize.height));
259 mOrientation = nsIPrintSettings::kLandscapeOrientation;
260 SetPaperWidth(PaperSizeFromCocoaPoints(paperSize.height));
261 SetPaperHeight(PaperSizeFromCocoaPoints(paperSize.width));
264 mUnwriteableMargin.top = static_cast<int32_t>([aPrintInfo topMargin]);
265 mUnwriteableMargin.right = static_cast<int32_t>([aPrintInfo rightMargin]);
266 mUnwriteableMargin.bottom = static_cast<int32_t>([aPrintInfo bottomMargin]);
267 mUnwriteableMargin.left = static_cast<int32_t>([aPrintInfo leftMargin]);
269 if (aAdoptPrintInfo) {
270 // Keep a reference to the printInfo; it may have settings that we don't
271 // know how to handle otherwise.
272 if (mSystemPrintInfo != aPrintInfo) {
273 if (mSystemPrintInfo) {
274 [mSystemPrintInfo release];
276 mSystemPrintInfo = aPrintInfo;
277 [mSystemPrintInfo retain];
280 // Clear any stored printInfo.
281 if (mSystemPrintInfo) {
282 [mSystemPrintInfo release];
283 mSystemPrintInfo = nullptr;
287 nsCocoaUtils::GetStringForNSString([[aPrintInfo printer] name], mPrinter);
289 // Only get the scaling value if shrink-to-fit is not selected:
290 bool isShrinkToFitChecked;
291 GetShrinkToFit(&isShrinkToFitChecked);
292 if (!isShrinkToFitChecked) {
293 // Limit scaling precision to whole percentage values.
294 mScaling = round(double([aPrintInfo scalingFactor]) * 100.0) / 100.0;
297 mOutputDestination = [&] {
298 if ([aPrintInfo jobDisposition] == NSPrintSaveJob) {
299 return kOutputDestinationFile;
301 return kOutputDestinationPrinter;
304 NSDictionary* dict = [aPrintInfo dictionary];
305 const char* filePath =
306 [[dict objectForKey:NSPrintJobSavingURL] fileSystemRepresentation];
307 if (filePath && *filePath) {
308 CopyUTF8toUTF16(Span(filePath, strlen(filePath)), mToFileName);
311 nsCocoaUtils::GetStringForNSString([aPrintInfo jobDisposition], mDisposition);
313 mNumCopies = [[dict objectForKey:NSPrintCopies] intValue];
315 if (![[dict objectForKey:NSPrintAllPages] boolValue]) {
316 mPageRanges.AppendElement([[dict objectForKey:NSPrintFirstPage] intValue]);
317 mPageRanges.AppendElement([[dict objectForKey:NSPrintLastPage] intValue]);
320 NSDictionary* printSettings = [aPrintInfo printSettings];
322 [printSettings objectForKey:@"com_apple_print_PrintSettings_PMDuplexing"];
324 PMDuplexMode duplexSetting = [value unsignedShortValue];
325 switch (duplexSetting) {
327 // An unknown value is treated as None.
328 MOZ_FALLTHROUGH_ASSERT("Unknown duplex value");
330 mDuplex = kDuplexNone;
332 case kPMDuplexNoTumble:
333 mDuplex = kDuplexFlipOnLongEdge;
335 case kPMDuplexTumble:
336 mDuplex = kDuplexFlipOnShortEdge;
340 // By default a printSettings dictionary doesn't initially contain the
341 // duplex key at all, so this is not an error; its absence just means no
342 // duplexing has been requested, so we return kDuplexNone.
343 mDuplex = kDuplexNone;
346 value = [printSettings
347 objectForKey:@"com_apple_print_PrintSettings_PMDestinationType"];
349 mDestination = [value unsignedShortValue];
352 const bool color = [&] {
353 if (StaticPrefs::print_cups_monochrome_enabled()) {
354 for (const auto& setting : kKnownMonochromeSettings) {
355 NSString* value = [printSettings objectForKey:setting.mName];
359 if ([setting.mValue isEqualToString:value]) {
367 SetPrintInColor(color);
369 SetIsInitializedFromPrinter(true);
371 NS_OBJC_END_TRY_IGNORE_BLOCK;