summaryrefslogtreecommitdiffstats
path: root/widget/cocoa/nsDeviceContextSpecX.mm
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--widget/cocoa/nsDeviceContextSpecX.mm303
1 files changed, 303 insertions, 0 deletions
diff --git a/widget/cocoa/nsDeviceContextSpecX.mm b/widget/cocoa/nsDeviceContextSpecX.mm
new file mode 100644
index 0000000000..0d8fb4140d
--- /dev/null
+++ b/widget/cocoa/nsDeviceContextSpecX.mm
@@ -0,0 +1,303 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsDeviceContextSpecX.h"
+
+#import <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <unistd.h>
+
+#ifdef MOZ_ENABLE_SKIA_PDF
+# include "mozilla/gfx/PrintTargetSkPDF.h"
+#endif
+#include "mozilla/gfx/PrintTargetCG.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Telemetry.h"
+
+#include "AppleUtils.h"
+#include "nsCocoaUtils.h"
+#include "nsCRT.h"
+#include "nsCUPSShim.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsILocalFileMac.h"
+#include "nsPaper.h"
+#include "nsPrinterListCUPS.h"
+#include "nsPrintSettingsX.h"
+#include "nsQueryObject.h"
+#include "prenv.h"
+
+// This must be the last include:
+#include "nsObjCExceptions.h"
+
+using namespace mozilla;
+using mozilla::gfx::IntSize;
+using mozilla::gfx::PrintTarget;
+using mozilla::gfx::PrintTargetCG;
+#ifdef MOZ_ENABLE_SKIA_PDF
+using mozilla::gfx::PrintTargetSkPDF;
+#endif
+using mozilla::gfx::SurfaceFormat;
+
+static LazyLogModule sDeviceContextSpecXLog("DeviceContextSpecX");
+
+//----------------------------------------------------------------------
+// nsDeviceContentSpecX
+
+nsDeviceContextSpecX::nsDeviceContextSpecX()
+ : mPrintSession(nullptr),
+ mPageFormat(nullptr),
+ mPrintSettings(nullptr)
+#ifdef MOZ_ENABLE_SKIA_PDF
+ ,
+ mPrintViaSkPDF(false)
+#endif
+{
+}
+
+nsDeviceContextSpecX::~nsDeviceContextSpecX() {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ if (mPrintSession) {
+ ::PMRelease(mPrintSession);
+ }
+ if (mPageFormat) {
+ ::PMRelease(mPageFormat);
+ }
+ if (mPrintSettings) {
+ ::PMRelease(mPrintSettings);
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+NS_IMPL_ISUPPORTS(nsDeviceContextSpecX, nsIDeviceContextSpec)
+
+NS_IMETHODIMP nsDeviceContextSpecX::Init(nsIWidget* aWidget, nsIPrintSettings* aPS,
+ bool aIsPrintPreview) {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ RefPtr<nsPrintSettingsX> settings(do_QueryObject(aPS));
+ if (!settings) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ bool toFile;
+ settings->GetPrintToFile(&toFile);
+
+ NSPrintInfo* printInfo = settings->CreateOrCopyPrintInfo();
+ if (!printInfo) {
+ return NS_ERROR_FAILURE;
+ }
+ mPrintSession = static_cast<PMPrintSession>([printInfo PMPrintSession]);
+ mPageFormat = static_cast<PMPageFormat>([printInfo PMPageFormat]);
+ mPrintSettings = static_cast<PMPrintSettings>([printInfo PMPrintSettings]);
+ MOZ_ASSERT(mPrintSession && mPageFormat && mPrintSettings);
+ ::PMRetain(mPrintSession);
+ ::PMRetain(mPageFormat);
+ ::PMRetain(mPrintSettings);
+ [printInfo release];
+
+#ifdef MOZ_ENABLE_SKIA_PDF
+ nsAutoString printViaPdf;
+ mozilla::Preferences::GetString("print.print_via_pdf_encoder", printViaPdf);
+ if (printViaPdf.EqualsLiteral("skia-pdf")) {
+ // Annoyingly, PMPrinterPrintWithFile does not pay attention to the
+ // kPMDestination* value set in the PMPrintSession; it always sends the PDF
+ // to the specified printer. This means that if we create the PDF using
+ // SkPDF then we need to manually handle user actions like "Open PDF in
+ // Preview" and "Save as PDF...".
+ // TODO: Currently we do not support using SkPDF for kPMDestinationFax or
+ // kPMDestinationProcessPDF ("Add PDF to iBooks, etc.), and we only support
+ // it for kPMDestinationFile if the destination file is a PDF.
+ // XXX Could PMWorkflowSubmitPDFWithSettings/PMPrinterPrintWithProvider help?
+ OSStatus status = noErr;
+ PMDestinationType destination;
+ status = ::PMSessionGetDestinationType(mPrintSession, mPrintSettings, &destination);
+ if (status == noErr) {
+ if (destination == kPMDestinationPrinter || destination == kPMDestinationPreview) {
+ mPrintViaSkPDF = true;
+ } else if (destination == kPMDestinationFile) {
+ AutoCFRelease<CFURLRef> destURL(nullptr);
+ status =
+ ::PMSessionCopyDestinationLocation(mPrintSession, mPrintSettings, destURL.receive());
+ if (status == noErr) {
+ AutoCFRelease<CFStringRef> destPathRef =
+ CFURLCopyFileSystemPath(destURL, kCFURLPOSIXPathStyle);
+ NSString* destPath = (NSString*)CFStringRef(destPathRef);
+ NSString* destPathExt = [destPath pathExtension];
+ if ([destPathExt isEqualToString:@"pdf"]) {
+ mPrintViaSkPDF = true;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ int16_t outputFormat;
+ aPS->GetOutputFormat(&outputFormat);
+
+ if (outputFormat == nsIPrintSettings::kOutputFormatPDF) {
+ // We don't actually currently support/use kOutputFormatPDF on mac, but
+ // this is for completeness in case we add that (we probably need to in
+ // order to support adding links into saved PDFs, for example).
+ Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE, u"pdf_file"_ns, 1);
+ } else {
+ PMDestinationType destination;
+ OSStatus status = ::PMSessionGetDestinationType(mPrintSession, mPrintSettings, &destination);
+ if (status == noErr &&
+ (destination == kPMDestinationFile || destination == kPMDestinationPreview ||
+ destination == kPMDestinationProcessPDF)) {
+ Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE, u"pdf_file"_ns, 1);
+ } else {
+ Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE, u"unknown"_ns, 1);
+ }
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP nsDeviceContextSpecX::BeginDocument(const nsAString& aTitle,
+ const nsAString& aPrintToFileName,
+ int32_t aStartPage, int32_t aEndPage) {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP nsDeviceContextSpecX::EndDocument() {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+#ifdef MOZ_ENABLE_SKIA_PDF
+ if (mPrintViaSkPDF) {
+ OSStatus status = noErr;
+
+ nsCOMPtr<nsILocalFileMac> tmpPDFFile = do_QueryInterface(mTempFile);
+ if (!tmpPDFFile) {
+ return NS_ERROR_FAILURE;
+ }
+ AutoCFRelease<CFURLRef> pdfURL(nullptr);
+ // Note that the caller is responsible to release pdfURL according to nsILocalFileMac.idl,
+ // even though we didn't follow the Core Foundation naming conventions here (the method
+ // should've been called CopyCFURL).
+ nsresult rv = tmpPDFFile->GetCFURL(pdfURL.receive());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PMDestinationType destination;
+ status = ::PMSessionGetDestinationType(mPrintSession, mPrintSettings, &destination);
+
+ switch (destination) {
+ case kPMDestinationPrinter: {
+ PMPrinter currentPrinter = NULL;
+ status = ::PMSessionGetCurrentPrinter(mPrintSession, &currentPrinter);
+ if (status != noErr) {
+ return NS_ERROR_FAILURE;
+ }
+ CFStringRef mimeType = CFSTR("application/pdf");
+ status =
+ ::PMPrinterPrintWithFile(currentPrinter, mPrintSettings, mPageFormat, mimeType, pdfURL);
+ break;
+ }
+ case kPMDestinationPreview: {
+ // XXXjwatt Or should we use CocoaFileUtils::RevealFileInFinder(pdfURL);
+ AutoCFRelease<CFStringRef> pdfPath = CFURLCopyFileSystemPath(pdfURL, kCFURLPOSIXPathStyle);
+ NSString* path = (NSString*)CFStringRef(pdfPath);
+ NSWorkspace* ws = [NSWorkspace sharedWorkspace];
+ [ws openFile:path];
+ break;
+ }
+ case kPMDestinationFile: {
+ AutoCFRelease<CFURLRef> destURL(nullptr);
+ status =
+ ::PMSessionCopyDestinationLocation(mPrintSession, mPrintSettings, destURL.receive());
+ if (status == noErr) {
+ AutoCFRelease<CFStringRef> sourcePathRef =
+ CFURLCopyFileSystemPath(pdfURL, kCFURLPOSIXPathStyle);
+ NSString* sourcePath = (NSString*)CFStringRef(sourcePathRef);
+# ifdef DEBUG
+ AutoCFRelease<CFStringRef> destPathRef =
+ CFURLCopyFileSystemPath(destURL, kCFURLPOSIXPathStyle);
+ NSString* destPath = (NSString*)CFStringRef(destPathRef);
+ NSString* destPathExt = [destPath pathExtension];
+ MOZ_ASSERT([destPathExt isEqualToString:@"pdf"],
+ "nsDeviceContextSpecX::Init only allows '.pdf' for now");
+ // We could use /usr/sbin/cupsfilter to convert the PDF to PS, but
+ // currently we don't.
+# endif
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ if ([fileManager fileExistsAtPath:sourcePath]) {
+ NSURL* src = static_cast<NSURL*>(CFURLRef(pdfURL));
+ NSURL* dest = static_cast<NSURL*>(CFURLRef(destURL));
+ bool ok = [fileManager replaceItemAtURL:dest
+ withItemAtURL:src
+ backupItemName:nil
+ options:NSFileManagerItemReplacementUsingNewMetadataOnly
+ resultingItemURL:nil
+ error:nil];
+ if (!ok) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ MOZ_ASSERT_UNREACHABLE("nsDeviceContextSpecX::Init doesn't set "
+ "mPrintViaSkPDF for other values");
+ }
+
+ return (status == noErr) ? NS_OK : NS_ERROR_FAILURE;
+ }
+#endif
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+void nsDeviceContextSpecX::GetPaperRect(double* aTop, double* aLeft, double* aBottom,
+ double* aRight) {
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ PMRect paperRect;
+ ::PMGetAdjustedPaperRect(mPageFormat, &paperRect);
+
+ *aTop = paperRect.top;
+ *aLeft = paperRect.left;
+ *aBottom = paperRect.bottom;
+ *aRight = paperRect.right;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+already_AddRefed<PrintTarget> nsDeviceContextSpecX::MakePrintTarget() {
+ double top, left, bottom, right;
+ GetPaperRect(&top, &left, &bottom, &right);
+ const double width = right - left;
+ const double height = bottom - top;
+ IntSize size = IntSize::Ceil(width, height);
+
+#ifdef MOZ_ENABLE_SKIA_PDF
+ if (mPrintViaSkPDF) {
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mTempFile));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ nsAutoCString tempPath("tmp-printing.pdf");
+ mTempFile->AppendNative(tempPath);
+ rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ mTempFile->GetNativePath(tempPath);
+ auto stream = MakeUnique<SkFILEWStream>(tempPath.get());
+ return PrintTargetSkPDF::CreateOrNull(std::move(stream), size);
+ }
+#endif
+
+ return PrintTargetCG::CreateOrNull(mPrintSession, mPageFormat, mPrintSettings, size);
+}