Commit b0fb1722 authored by Emilio Cobos Álvarez's avatar Emilio Cobos Álvarez
Browse files

Bug 1760836 - Support printing to an nsIOutputStream....

Bug 1760836 - Support printing to an nsIOutputStream. r=jfkthame,jrmuizel,webdriver-reviewers,geckoview-reviewers,agi

The trickiest bits are the PrintTargetCG ones, the rest is just plumbing
and cleanups and tests, but let me know if you want those to be split
out, can do.

The GTK change to nsPrintSettingsGTK::GetResolution is a no-op (we only
read resolution on windows), but I did that because we assume that it
doesn't fail and GTK returns a sane default anyways.

Differential Revision: https://phabricator.services.mozilla.com/D142199
parent 9480f327
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1351,7 +1351,8 @@ this.tabs = class extends ExtensionAPI {
                printSettings.isInitializedFromPrinter = true;
                printSettings.isInitializedFromPrefs = true;

                printSettings.printToFile = true;
                printSettings.outputDestination =
                  Ci.nsIPrintSettings.kOutputDestinationFile;
                printSettings.toFileName = picker.file.path;

                printSettings.printSilent = true;
+10 −7
Original line number Diff line number Diff line
@@ -9,8 +9,9 @@
#include <Carbon/Carbon.h>
#include "PrintTarget.h"

namespace mozilla {
namespace gfx {
class nsIOutputStream;

namespace mozilla::gfx {

/**
 * CoreGraphics printing target.
@@ -18,8 +19,9 @@ namespace gfx {
class PrintTargetCG final : public PrintTarget {
 public:
  static already_AddRefed<PrintTargetCG> CreateOrNull(
      PMPrintSession aPrintSession, PMPageFormat aPageFormat,
      PMPrintSettings aPrintSettings, const IntSize& aSize);
      nsIOutputStream* aOutputStream, PMPrintSession aPrintSession,
      PMPageFormat aPageFormat, PMPrintSettings aPrintSettings,
      const IntSize& aSize);

  nsresult BeginPrinting(const nsAString& aTitle,
                         const nsAString& aPrintToFileName, int32_t aStartPage,
@@ -32,16 +34,17 @@ class PrintTargetCG final : public PrintTarget {
  already_AddRefed<DrawTarget> GetReferenceDrawTarget() final;

 private:
  PrintTargetCG(PMPrintSession aPrintSession, PMPageFormat aPageFormat,
  PrintTargetCG(CGContextRef aPrintToStreamContext,
                PMPrintSession aPrintSession, PMPageFormat aPageFormat,
                PMPrintSettings aPrintSettings, const IntSize& aSize);
  ~PrintTargetCG();

  CGContextRef mPrintToStreamContext = nullptr;
  PMPrintSession mPrintSession;
  PMPageFormat mPageFormat;
  PMPrintSettings mPrintSettings;
};

}  // namespace gfx
}  // namespace mozilla
}  // namespace mozilla::gfx

#endif /* MOZILLA_GFX_PRINTTARGETCG_H */
+103 −25
Original line number Diff line number Diff line
@@ -10,13 +10,17 @@
#include "mozilla/gfx/HelpersCairo.h"
#include "nsObjCExceptions.h"
#include "nsString.h"
#include "nsIOutputStream.h"

namespace mozilla {
namespace gfx {
namespace mozilla::gfx {

PrintTargetCG::PrintTargetCG(PMPrintSession aPrintSession, PMPageFormat aPageFormat,
                             PMPrintSettings aPrintSettings, const IntSize& aSize)
static size_t PutBytesNull(void* info, const void* buffer, size_t count) { return count; }

PrintTargetCG::PrintTargetCG(CGContextRef aPrintToStreamContext, PMPrintSession aPrintSession,
                             PMPageFormat aPageFormat, PMPrintSettings aPrintSettings,
                             const IntSize& aSize)
    : PrintTarget(/* aCairoSurface */ nullptr, aSize),
      mPrintToStreamContext(aPrintToStreamContext),
      mPrintSession(aPrintSession),
      mPageFormat(aPageFormat),
      mPrintSettings(aPrintSettings) {
@@ -41,24 +45,80 @@ PrintTargetCG::~PrintTargetCG() {
  ::PMRelease(mPageFormat);
  ::PMRelease(mPrintSettings);

  if (mPrintToStreamContext) {
    CGContextRelease(mPrintToStreamContext);
  }

  NS_OBJC_END_TRY_IGNORE_BLOCK;
}

/* static */ already_AddRefed<PrintTargetCG> PrintTargetCG::CreateOrNull(
    PMPrintSession aPrintSession, PMPageFormat aPageFormat, PMPrintSettings aPrintSettings,
static size_t WriteStreamBytes(void* aInfo, const void* aBuffer, size_t aCount) {
  auto* stream = static_cast<nsIOutputStream*>(aInfo);
  auto* data = static_cast<const char*>(aBuffer);
  size_t remaining = aCount;
  do {
    uint32_t wrote = 0;
    // Handle potential narrowing from size_t to uint32_t.
    uint32_t toWrite = uint32_t(std::min(remaining, size_t(std::numeric_limits<uint32_t>::max())));
    if (NS_WARN_IF(NS_FAILED(stream->Write(data, toWrite, &wrote)))) {
      break;
    }
    data += wrote;
    remaining -= size_t(wrote);
  } while (remaining);
  return aCount;
}

static void ReleaseStream(void* aInfo) {
  auto* stream = static_cast<nsIOutputStream*>(aInfo);
  stream->Close();
  NS_RELEASE(stream);
}

static CGContextRef CreatePrintToStreamContext(nsIOutputStream* aOutputStream,
                                               const IntSize& aSize) {
  MOZ_ASSERT(aOutputStream);

  NS_ADDREF(aOutputStream);  // Matched by the NS_RELEASE in ReleaseStream.

  CGRect pageBox{{0.0, 0.0}, {CGFloat(aSize.width), CGFloat(aSize.height)}};
  CGDataConsumerCallbacks callbacks = {WriteStreamBytes, ReleaseStream};
  CGDataConsumerRef consumer = CGDataConsumerCreate(aOutputStream, &callbacks);

  // This metadata is added by the CorePrinting APIs in the non-stream case.
  NSString* bundleName =
      [NSBundle.mainBundle.localizedInfoDictionary objectForKey:(NSString*)kCFBundleNameKey];
  CFMutableDictionaryRef auxiliaryInfo = CFDictionaryCreateMutable(
      kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  CFDictionaryAddValue(auxiliaryInfo, kCGPDFContextCreator, (__bridge CFStringRef)bundleName);

  CGContextRef pdfContext = CGPDFContextCreate(consumer, &pageBox, auxiliaryInfo);
  CGDataConsumerRelease(consumer);
  CFRelease(auxiliaryInfo);
  return pdfContext;
}

/* static */ already_AddRefed<PrintTargetCG> PrintTargetCG::CreateOrNull(
    nsIOutputStream* aOutputStream, PMPrintSession aPrintSession, PMPageFormat aPageFormat,
    PMPrintSettings aPrintSettings, const IntSize& aSize) {
  if (!Factory::CheckSurfaceSize(aSize)) {
    return nullptr;
  }

  CGContextRef printToStreamContext = nullptr;
  if (aOutputStream) {
    printToStreamContext = CreatePrintToStreamContext(aOutputStream, aSize);
    if (!printToStreamContext) {
      return nullptr;
    }
  }

  RefPtr<PrintTargetCG> target =
      new PrintTargetCG(aPrintSession, aPageFormat, aPrintSettings, aSize);
      new PrintTargetCG(printToStreamContext, aPrintSession, aPageFormat, aPrintSettings, aSize);

  return target.forget();
}

static size_t PutBytesNull(void* info, const void* buffer, size_t count) { return count; }

already_AddRefed<DrawTarget> PrintTargetCG::GetReferenceDrawTarget() {
  if (!mRefDT) {
    const IntSize size(1, 1);
@@ -95,6 +155,10 @@ nsresult PrintTargetCG::BeginPrinting(const nsAString& aTitle, const nsAString&
                                      int32_t aStartPage, int32_t aEndPage) {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  if (mPrintToStreamContext) {
    return NS_OK;
  }

  // Print Core of Application Service sent print job with names exceeding
  // 255 bytes. This is a workaround until fix it.
  // (https://openradar.appspot.com/34428043)
@@ -127,6 +191,12 @@ nsresult PrintTargetCG::BeginPrinting(const nsAString& aTitle, const nsAString&
nsresult PrintTargetCG::EndPrinting() {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  if (mPrintToStreamContext) {
    CGContextFlush(mPrintToStreamContext);
    CGPDFContextClose(mPrintToStreamContext);
    return NS_OK;
  }

  ::PMSessionEndDocumentNoDialog(mPrintSession);
  return NS_OK;

@@ -143,13 +213,17 @@ nsresult PrintTargetCG::AbortPrinting() {
nsresult PrintTargetCG::BeginPage() {
  NS_OBJC_BEGIN_TRY_BLOCK_RETURN;

  CGContextRef context;
  if (mPrintToStreamContext) {
    CGContextBeginPage(mPrintToStreamContext, nullptr);
    context = mPrintToStreamContext;
  } else {
    PMSessionError(mPrintSession);
  OSStatus status = ::PMSessionBeginPageNoDialog(mPrintSession, mPageFormat, NULL);
    OSStatus status = ::PMSessionBeginPageNoDialog(mPrintSession, mPageFormat, nullptr);
    if (status != noErr) {
      return NS_ERROR_ABORT;
    }

  CGContextRef context;
    // This call will fail if it wasn't called between the PMSessionBeginPage/
    // PMSessionEndPage calls:
    ::PMSessionGetCGGraphicsContext(mPrintSession, &context);
@@ -157,6 +231,7 @@ nsresult PrintTargetCG::BeginPage() {
    if (!context) {
      return NS_ERROR_FAILURE;
    }
  }

  unsigned int width = static_cast<unsigned int>(mSize.width);
  unsigned int height = static_cast<unsigned int>(mSize.height);
@@ -185,15 +260,18 @@ nsresult PrintTargetCG::EndPage() {
  cairo_surface_finish(mCairoSurface);
  mCairoSurface = nullptr;

  if (mPrintToStreamContext) {
    CGContextEndPage(mPrintToStreamContext);
  } else {
    OSStatus status = ::PMSessionEndPageNoDialog(mPrintSession);
    if (status != noErr) {
      return NS_ERROR_ABORT;
    }
  }

  return PrintTarget::EndPage();

  NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
}

}  // namespace gfx
}  // namespace mozilla
}  // namespace mozilla::gfx
+4 −6
Original line number Diff line number Diff line
@@ -1080,10 +1080,9 @@ nsresult nsPrintJob::SetupToPrintContent() {

  nsAutoString fileNameStr;
  // check to see if we are printing to a file
  bool isPrintToFile = false;
  printData->mPrintSettings->GetPrintToFile(&isPrintToFile);
  if (isPrintToFile) {
    // On some platforms The BeginDocument needs to know the name of the file.
  if (printData->mPrintSettings->GetOutputDestination() ==
      nsIPrintSettings::kOutputDestinationFile) {
    // On some platforms the BeginDocument needs to know the name of the file.
    printData->mPrintSettings->GetToFileName(fileNameStr);
  }

@@ -2332,8 +2331,7 @@ nsresult nsPrintJob::StartPagePrintTimer(const UniquePtr<nsPrintObject>& aPO) {
  if (!mPagePrintTimer) {
    // Get the delay time in between the printing of each page
    // this gives the user more time to press cancel
    int32_t printPageDelay = 50;
    mPrt->mPrintSettings->GetPrintPageDelay(&printPageDelay);
    int32_t printPageDelay = mPrt->mPrintSettings->GetPrintPageDelay();

    nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
    NS_ENSURE_TRUE(cv, NS_ERROR_FAILURE);
+1 −1
Original line number Diff line number Diff line
@@ -1721,7 +1721,7 @@ function RecvStartPrint(isPrintSelection, printRange)
    ps.unwriteableMarginRight = 0;
    ps.unwriteableMarginLeft = 0;
    ps.unwriteableMarginBottom = 0;
    ps.printToFile = true;
    ps.outputDestination = Ci.nsIPrintSettings.kOutputDestinationFile;
    ps.toFileName = file.path;
    ps.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
    ps.printSelectionOnly = isPrintSelection;
Loading