summaryrefslogtreecommitdiffstats
path: root/widget/cocoa/nsPrintSettingsX.mm
diff options
context:
space:
mode:
Diffstat (limited to 'widget/cocoa/nsPrintSettingsX.mm')
-rw-r--r--widget/cocoa/nsPrintSettingsX.mm362
1 files changed, 362 insertions, 0 deletions
diff --git a/widget/cocoa/nsPrintSettingsX.mm b/widget/cocoa/nsPrintSettingsX.mm
new file mode 100644
index 0000000000..f7d6034f95
--- /dev/null
+++ b/widget/cocoa/nsPrintSettingsX.mm
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsPrintSettingsX.h"
+#include "nsObjCExceptions.h"
+
+#include "plbase64.h"
+
+#include "nsCocoaUtils.h"
+#include "nsXULAppAPI.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_print.h"
+#include "nsPrinterCUPS.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS_INHERITED(nsPrintSettingsX, nsPrintSettings, nsPrintSettingsX)
+
+nsPrintSettingsX::nsPrintSettingsX() {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ mDestination = kPMDestinationInvalid;
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+already_AddRefed<nsIPrintSettings> CreatePlatformPrintSettings(
+ const PrintSettingsInitializer& aSettings) {
+ RefPtr<nsPrintSettings> settings = new nsPrintSettingsX();
+ settings->InitWithInitializer(aSettings);
+ settings->SetDefaultFileName();
+ return settings.forget();
+}
+
+nsPrintSettingsX& nsPrintSettingsX::operator=(const nsPrintSettingsX& rhs) {
+ if (this == &rhs) {
+ return *this;
+ }
+
+ nsPrintSettings::operator=(rhs);
+
+ mDestination = rhs.mDestination;
+ mDisposition = rhs.mDisposition;
+
+ // We don't copy mSystemPrintInfo here, so any copied printSettings will start out
+ // without a wrapped printInfo, just using our internal settings. The system
+ // printInfo is used *only* by the nsPrintSettingsX to which it was originally
+ // passed (when the user ran a system print UI dialog).
+
+ return *this;
+}
+
+nsresult nsPrintSettingsX::_Clone(nsIPrintSettings** _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ auto newSettings = MakeRefPtr<nsPrintSettingsX>();
+ *newSettings = *this;
+ newSettings.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintSettingsX::_Assign(nsIPrintSettings* aPS) {
+ nsPrintSettingsX* printSettingsX = static_cast<nsPrintSettingsX*>(aPS);
+ if (!printSettingsX) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ *this = *printSettingsX;
+ return NS_OK;
+}
+
+struct KnownMonochromeSetting {
+ const NSString* mName;
+ const NSString* mValue;
+};
+
+#define DECLARE_KNOWN_MONOCHROME_SETTING(key_, value_) \
+ { @key_, @value_ } \
+ ,
+static const KnownMonochromeSetting kKnownMonochromeSettings[] = {
+ CUPS_EACH_MONOCHROME_PRINTER_SETTING(DECLARE_KNOWN_MONOCHROME_SETTING)};
+#undef DECLARE_KNOWN_MONOCHROME_SETTING
+
+NSPrintInfo* nsPrintSettingsX::CreateOrCopyPrintInfo(bool aWithScaling) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ // If we have a printInfo that came from the system print UI, use it so that
+ // any printer-specific settings we don't know about will still be used.
+ if (mSystemPrintInfo) {
+ NSPrintInfo* sysPrintInfo = [mSystemPrintInfo copy];
+ // Any required scaling will be done by Gecko, so we don't want it here.
+ [sysPrintInfo setScalingFactor:1.0f];
+ return sysPrintInfo;
+ }
+
+ // Note that the app shared `sharedPrintInfo` object is special! The system
+ // print dialog and print settings dialog update it with the values chosen
+ // by the user. Using that object here to initialize new nsPrintSettingsX
+ // objects could mask bugs in our code where we fail to save and/or restore
+ // print settings ourselves (e.g., bug 1636725). On other platforms we only
+ // initialize new nsPrintSettings objects from the settings that we save to
+ // prefs. Perhaps we should stop using sharedPrintInfo here for consistency?
+ NSPrintInfo* printInfo = [[NSPrintInfo sharedPrintInfo] copy];
+
+ NSSize paperSize;
+ if (GetSheetOrientation() == kPortraitOrientation) {
+ paperSize.width = CocoaPointsFromPaperSize(mPaperWidth);
+ paperSize.height = CocoaPointsFromPaperSize(mPaperHeight);
+ } else {
+ paperSize.width = CocoaPointsFromPaperSize(mPaperHeight);
+ paperSize.height = CocoaPointsFromPaperSize(mPaperWidth);
+ }
+ [printInfo setPaperSize:paperSize];
+
+ if (paperSize.width > paperSize.height) {
+ [printInfo setOrientation:NSPaperOrientationLandscape];
+ } else {
+ [printInfo setOrientation:NSPaperOrientationPortrait];
+ }
+
+ [printInfo setTopMargin:mUnwriteableMargin.top];
+ [printInfo setRightMargin:mUnwriteableMargin.right];
+ [printInfo setBottomMargin:mUnwriteableMargin.bottom];
+ [printInfo setLeftMargin:mUnwriteableMargin.left];
+
+ // If the printer name is the name of our pseudo print-to-PDF printer, the
+ // following `setPrinter` call will fail silently, since macOS doesn't know
+ // anything about it. That's OK, because mPrinter is our canonical source of
+ // truth.
+ // Actually, it seems Mac OS X 10.12 (the oldest version of Mac that we
+ // support) hangs if the printer name is not recognized. For now we explicitly
+ // check for our print-to-PDF printer, but that is not ideal since we should
+ // really localize the name of this printer at some point. Once we drop
+ // support for 10.12 we should remove this check.
+ if (!mPrinter.EqualsLiteral("Mozilla Save to PDF")) {
+ [printInfo setPrinter:[NSPrinter printerWithName:nsCocoaUtils::ToNSString(mPrinter)]];
+ }
+
+ // Scaling is handled by gecko, we do NOT want the cocoa printing system to add
+ // a second scaling on top of that. So we only set the true scaling factor here
+ // if the caller explicitly asked for it.
+ [printInfo setScalingFactor:CGFloat(aWithScaling ? mScaling : 1.0f)];
+
+ const bool allPages = mPageRanges.IsEmpty();
+
+ NSMutableDictionary* dict = [printInfo dictionary];
+ [dict setObject:[NSNumber numberWithInt:mNumCopies] forKey:NSPrintCopies];
+ [dict setObject:[NSNumber numberWithBool:allPages] forKey:NSPrintAllPages];
+
+ int32_t start = 1;
+ int32_t end = 1;
+ for (size_t i = 0; i < mPageRanges.Length(); i += 2) {
+ start = std::min(start, mPageRanges[i]);
+ end = std::max(end, mPageRanges[i + 1]);
+ }
+
+ [dict setObject:[NSNumber numberWithInt:start] forKey:NSPrintFirstPage];
+ [dict setObject:[NSNumber numberWithInt:end] forKey:NSPrintLastPage];
+
+ NSURL* jobSavingURL = nullptr;
+ if (!mToFileName.IsEmpty()) {
+ jobSavingURL = [NSURL fileURLWithPath:nsCocoaUtils::ToNSString(mToFileName)];
+ if (jobSavingURL) {
+ // Note: the PMPrintSettingsSetJobName call in nsPrintDialogServiceX::Show
+ // seems to mean that we get a sensible file name pre-populated in the
+ // dialog there, although our mToFileName is expected to be a full path,
+ // and it's less clear where the rest of the path (the directory to save
+ // to) in nsPrintDialogServiceX::Show comes from (perhaps from the use
+ // of `sharedPrintInfo` to initialize new nsPrintSettingsX objects).
+ [dict setObject:jobSavingURL forKey:NSPrintJobSavingURL];
+ }
+ }
+
+ if (mDisposition.IsEmpty()) {
+ // NOTE: It's unclear what to do for kOutputDestinationStream but this is
+ // only for the native print dialog where that can't happen.
+ if (mOutputDestination == kOutputDestinationFile) {
+ [printInfo setJobDisposition:NSPrintSaveJob];
+ } else {
+ [printInfo setJobDisposition:NSPrintSpoolJob];
+ }
+ } else {
+ [printInfo setJobDisposition:nsCocoaUtils::ToNSString(mDisposition)];
+ }
+
+ PMDuplexMode duplexSetting;
+ switch (mDuplex) {
+ default:
+ // This can't happen :) but if it does, we treat it as "none".
+ MOZ_FALLTHROUGH_ASSERT("Unknown duplex value");
+ case kDuplexNone:
+ duplexSetting = kPMDuplexNone;
+ break;
+ case kDuplexFlipOnLongEdge:
+ duplexSetting = kPMDuplexNoTumble;
+ break;
+ case kDuplexFlipOnShortEdge:
+ duplexSetting = kPMDuplexTumble;
+ break;
+ }
+
+ NSMutableDictionary* printSettings = [printInfo printSettings];
+ [printSettings setObject:[NSNumber numberWithUnsignedShort:duplexSetting]
+ forKey:@"com_apple_print_PrintSettings_PMDuplexing"];
+
+ if (mDestination != kPMDestinationInvalid) {
+ // Required to support PDF-workflow destinations such as Save to Web Receipts.
+ [printSettings setObject:[NSNumber numberWithUnsignedShort:mDestination]
+ forKey:@"com_apple_print_PrintSettings_PMDestinationType"];
+ if (jobSavingURL) {
+ [printSettings setObject:[jobSavingURL absoluteString]
+ forKey:@"com_apple_print_PrintSettings_PMOutputFilename"];
+ }
+ }
+
+ if (StaticPrefs::print_cups_monochrome_enabled() && !GetPrintInColor()) {
+ for (const auto& setting : kKnownMonochromeSettings) {
+ [printSettings setObject:setting.mValue forKey:setting.mName];
+ }
+ auto applySetting = [&](const nsACString& aKey, const nsACString& aValue) {
+ [printSettings setObject:nsCocoaUtils::ToNSString(aValue)
+ forKey:nsCocoaUtils::ToNSString(aKey)];
+ };
+ nsPrinterCUPS::ForEachExtraMonochromeSetting(applySetting);
+ }
+
+ return printInfo;
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(nullptr);
+}
+
+void nsPrintSettingsX::SetFromPrintInfo(NSPrintInfo* aPrintInfo, bool aAdoptPrintInfo) {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ // Set page-size/margins.
+ NSSize paperSize = [aPrintInfo paperSize];
+ const bool areSheetsOfPaperPortraitMode =
+ ([aPrintInfo orientation] == NSPaperOrientationPortrait);
+
+ // If our MacOS print settings say that we're producing portrait-mode sheets
+ // of paper, then our page format must also be portrait-mode; unless we've
+ // got a pages-per-sheet value with orthogonal pages/sheets, in which case
+ // it's reversed.
+ const bool arePagesPortraitMode = (areSheetsOfPaperPortraitMode != HasOrthogonalSheetsAndPages());
+
+ if (arePagesPortraitMode) {
+ mOrientation = nsIPrintSettings::kPortraitOrientation;
+ SetPaperWidth(PaperSizeFromCocoaPoints(paperSize.width));
+ SetPaperHeight(PaperSizeFromCocoaPoints(paperSize.height));
+ } else {
+ mOrientation = nsIPrintSettings::kLandscapeOrientation;
+ SetPaperWidth(PaperSizeFromCocoaPoints(paperSize.height));
+ SetPaperHeight(PaperSizeFromCocoaPoints(paperSize.width));
+ }
+
+ mUnwriteableMargin.top = [aPrintInfo topMargin];
+ mUnwriteableMargin.right = [aPrintInfo rightMargin];
+ mUnwriteableMargin.bottom = [aPrintInfo bottomMargin];
+ mUnwriteableMargin.left = [aPrintInfo leftMargin];
+
+ if (aAdoptPrintInfo) {
+ // Keep a reference to the printInfo; it may have settings that we don't know how to handle
+ // otherwise.
+ if (mSystemPrintInfo != aPrintInfo) {
+ if (mSystemPrintInfo) {
+ [mSystemPrintInfo release];
+ }
+ mSystemPrintInfo = aPrintInfo;
+ [mSystemPrintInfo retain];
+ }
+ } else {
+ // Clear any stored printInfo.
+ if (mSystemPrintInfo) {
+ [mSystemPrintInfo release];
+ mSystemPrintInfo = nullptr;
+ }
+ }
+
+ nsCocoaUtils::GetStringForNSString([[aPrintInfo printer] name], mPrinter);
+
+ // Only get the scaling value if shrink-to-fit is not selected:
+ bool isShrinkToFitChecked;
+ GetShrinkToFit(&isShrinkToFitChecked);
+ if (!isShrinkToFitChecked) {
+ // Limit scaling precision to whole percentage values.
+ mScaling = round(double([aPrintInfo scalingFactor]) * 100.0) / 100.0;
+ }
+
+ mOutputDestination = [&] {
+ if ([aPrintInfo jobDisposition] == NSPrintSaveJob) {
+ return kOutputDestinationFile;
+ }
+ return kOutputDestinationPrinter;
+ }();
+
+ NSDictionary* dict = [aPrintInfo dictionary];
+ const char* filePath = [[dict objectForKey:NSPrintJobSavingURL] fileSystemRepresentation];
+ if (filePath && *filePath) {
+ CopyUTF8toUTF16(Span(filePath, strlen(filePath)), mToFileName);
+ }
+
+ nsCocoaUtils::GetStringForNSString([aPrintInfo jobDisposition], mDisposition);
+
+ mNumCopies = [[dict objectForKey:NSPrintCopies] intValue];
+ mPageRanges.Clear();
+ if (![[dict objectForKey:NSPrintAllPages] boolValue]) {
+ mPageRanges.AppendElement([[dict objectForKey:NSPrintFirstPage] intValue]);
+ mPageRanges.AppendElement([[dict objectForKey:NSPrintLastPage] intValue]);
+ }
+
+ NSDictionary* printSettings = [aPrintInfo printSettings];
+ NSNumber* value = [printSettings objectForKey:@"com_apple_print_PrintSettings_PMDuplexing"];
+ if (value) {
+ PMDuplexMode duplexSetting = [value unsignedShortValue];
+ switch (duplexSetting) {
+ default:
+ // An unknown value is treated as None.
+ MOZ_FALLTHROUGH_ASSERT("Unknown duplex value");
+ case kPMDuplexNone:
+ mDuplex = kDuplexNone;
+ break;
+ case kPMDuplexNoTumble:
+ mDuplex = kDuplexFlipOnLongEdge;
+ break;
+ case kPMDuplexTumble:
+ mDuplex = kDuplexFlipOnShortEdge;
+ break;
+ }
+ } else {
+ // By default a printSettings dictionary doesn't initially contain the
+ // duplex key at all, so this is not an error; its absence just means no
+ // duplexing has been requested, so we return kDuplexNone.
+ mDuplex = kDuplexNone;
+ }
+
+ value = [printSettings objectForKey:@"com_apple_print_PrintSettings_PMDestinationType"];
+ if (value) {
+ mDestination = [value unsignedShortValue];
+ }
+
+ const bool color = [&] {
+ if (StaticPrefs::print_cups_monochrome_enabled()) {
+ for (const auto& setting : kKnownMonochromeSettings) {
+ NSString* value = [printSettings objectForKey:setting.mName];
+ if (!value) {
+ continue;
+ }
+ if ([setting.mValue isEqualToString:value]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }();
+
+ SetPrintInColor(color);
+
+ SetIsInitializedFromPrinter(true);
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}