зеркало из https://github.com/mozilla/gecko-dev.git
432 строки
15 KiB
Plaintext
432 строки
15 KiB
Plaintext
/* -*- 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 "plstr.h"
|
|
|
|
#include "nsCocoaUtils.h"
|
|
#include "nsXULAppAPI.h"
|
|
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/StaticPrefs_print.h"
|
|
#include "nsPrinterCUPS.h"
|
|
|
|
using namespace mozilla;
|
|
|
|
#define MAC_OS_X_PAGE_SETUP_PREFNAME "print.macosx.pagesetup-2"
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(nsPrintSettingsX, nsPrintSettings, nsPrintSettingsX)
|
|
|
|
nsPrintSettingsX::nsPrintSettingsX() {
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
mDestination = kPMDestinationInvalid;
|
|
|
|
/*
|
|
* Don't save print settings after the user cancels out of the
|
|
* print dialog. For saving print settings after a cancellation
|
|
* to work properly, in addition to changing |mSaveOnCancel|,
|
|
* the print dialog implementation must be updated to save changed
|
|
* settings and serialize them back to the child process.
|
|
*/
|
|
mSaveOnCancel = false;
|
|
|
|
NS_OBJC_END_TRY_ABORT_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::ReadPageFormatFromPrefs() {
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
nsAutoCString encodedData;
|
|
nsresult rv = Preferences::GetCString(MAC_OS_X_PAGE_SETUP_PREFNAME, encodedData);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// decode the base64
|
|
char* decodedData = PL_Base64Decode(encodedData.get(), encodedData.Length(), nullptr);
|
|
NSData* data = [NSData dataWithBytes:decodedData length:strlen(decodedData)];
|
|
if (!data) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
PMPageFormat newPageFormat;
|
|
OSStatus status = ::PMPageFormatCreateWithDataRepresentation((CFDataRef)data, &newPageFormat);
|
|
if (status == noErr) {
|
|
SetPMPageFormat(newPageFormat);
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
|
}
|
|
|
|
nsresult nsPrintSettingsX::WritePageFormatToPrefs() {
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
|
|
|
// XXX See jwatt's comment https://phabricator.services.mozilla.com/D94026#inline-534089:
|
|
// I feel like we should make WritePageFormatToPrefs a no-op if mSystemPrintInfo isn't set,
|
|
// but I'm not sure. We'll be saving the other settings to prefs, after all.
|
|
// But then I guess we have no way to know whether we should call ReadPageFormatFromPrefs or not
|
|
// in nsPrintSettingsServiceX::ReadPrefs, so I guess we have to set it just to avoid setting that
|
|
// read reading a stale PMPageFormat.
|
|
|
|
NSPrintInfo* printInfo = CreateOrCopyPrintInfo();
|
|
if (NS_WARN_IF(!printInfo)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
[printInfo autorelease];
|
|
|
|
PMPageFormat pageFormat = static_cast<PMPageFormat>([printInfo PMPageFormat]);
|
|
|
|
NSData* data = nil;
|
|
OSStatus err = ::PMPageFormatCreateDataRepresentation(pageFormat, (CFDataRef*)&data,
|
|
kPMDataFormatXMLDefault);
|
|
if (err != noErr) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsAutoCString encodedData;
|
|
encodedData.Adopt(PL_Base64Encode((char*)[data bytes], [data length], nullptr));
|
|
if (!encodedData.get()) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return Preferences::SetCString(MAC_OS_X_PAGE_SETUP_PREFNAME, encodedData);
|
|
|
|
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
|
}
|
|
|
|
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
|
|
|
|
void nsPrintSettingsX::SetPMPageFormat(PMPageFormat aPageFormat) {
|
|
// Get a printInfo based on our current properties.
|
|
NSPrintInfo* printInfo = CreateOrCopyPrintInfo();
|
|
if (NS_WARN_IF(!printInfo)) {
|
|
return;
|
|
}
|
|
// Apply the given PMPageFormat to it.
|
|
PMPageFormat oldPageFormat = static_cast<PMPageFormat>([printInfo PMPageFormat]);
|
|
::PMCopyPageFormat(aPageFormat, oldPageFormat);
|
|
[printInfo updateFromPMPageFormat];
|
|
// And then update our internal properties to match the updated printInfo.
|
|
SetPageFormatFromPrintInfo(printInfo);
|
|
[printInfo release];
|
|
}
|
|
|
|
NSPrintInfo* nsPrintSettingsX::CreateOrCopyPrintInfo(bool aWithScaling) {
|
|
NS_OBJC_BEGIN_TRY_ABORT_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 (mOrientation == kPortraitOrientation) {
|
|
[printInfo setOrientation:NSPaperOrientationPortrait];
|
|
paperSize.width = CocoaPointsFromPaperSize(mPaperWidth);
|
|
paperSize.height = CocoaPointsFromPaperSize(mPaperHeight);
|
|
[printInfo setPaperSize:paperSize];
|
|
} else {
|
|
[printInfo setOrientation:NSPaperOrientationLandscape];
|
|
paperSize.width = CocoaPointsFromPaperSize(mPaperHeight);
|
|
paperSize.height = CocoaPointsFromPaperSize(mPaperWidth);
|
|
[printInfo setPaperSize:paperSize];
|
|
}
|
|
|
|
[printInfo setTopMargin:mUnwriteableMargin.top];
|
|
[printInfo setRightMargin:mUnwriteableMargin.right];
|
|
[printInfo setBottomMargin:mUnwriteableMargin.bottom];
|
|
[printInfo setLeftMargin:mUnwriteableMargin.left];
|
|
|
|
// If the name is our pseudo-printer "Mozilla Save to PDF", this will silently fail
|
|
// as no such printer is known. That's OK, because mPrinter will remain correct
|
|
// and is our canonical source of truth here.
|
|
[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()) {
|
|
if (mPrintToFile) {
|
|
[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 kSimplex:
|
|
duplexSetting = kPMDuplexNone;
|
|
break;
|
|
case kDuplexVertical:
|
|
duplexSetting = kPMDuplexTumble;
|
|
break;
|
|
case kDuplexHorizontal:
|
|
duplexSetting = kPMDuplexNoTumble;
|
|
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_ABORT_BLOCK_RETURN(nullptr);
|
|
}
|
|
|
|
void nsPrintSettingsX::SetPageFormatFromPrintInfo(const NSPrintInfo* aPrintInfo) {
|
|
NSSize paperSize = [aPrintInfo paperSize];
|
|
if ([aPrintInfo orientation] == NSPaperOrientationPortrait) {
|
|
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];
|
|
|
|
SetIsInitializedFromPrinter(true);
|
|
}
|
|
|
|
void nsPrintSettingsX::SetFromPrintInfo(NSPrintInfo* aPrintInfo, bool aAdoptPrintInfo) {
|
|
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
|
|
|
// Set page-size/margins. This also ensures the initedFromPrinter flag is set.
|
|
SetPageFormatFromPrintInfo(aPrintInfo);
|
|
|
|
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;
|
|
}
|
|
|
|
mPrintToFile = [aPrintInfo jobDisposition] == NSPrintSaveJob;
|
|
|
|
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 = kSimplex;
|
|
break;
|
|
case kPMDuplexNoTumble:
|
|
mDuplex = kDuplexHorizontal;
|
|
break;
|
|
case kPMDuplexTumble:
|
|
mDuplex = kDuplexVertical;
|
|
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 kSimplex.
|
|
mDuplex = kSimplex;
|
|
}
|
|
|
|
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_ABORT_BLOCK;
|
|
}
|