diff options
Diffstat (limited to 'widget/cocoa/nsPrintSettingsX.mm')
-rw-r--r-- | widget/cocoa/nsPrintSettingsX.mm | 362 |
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; +} |