summaryrefslogtreecommitdiffstats
path: root/widget/gtk/nsDeviceContextSpecG.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gtk/nsDeviceContextSpecG.cpp')
-rw-r--r--widget/gtk/nsDeviceContextSpecG.cpp498
1 files changed, 498 insertions, 0 deletions
diff --git a/widget/gtk/nsDeviceContextSpecG.cpp b/widget/gtk/nsDeviceContextSpecG.cpp
new file mode 100644
index 000000000..0bd87bd85
--- /dev/null
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -0,0 +1,498 @@
+/* -*- 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 "nsDeviceContextSpecG.h"
+
+#include "mozilla/gfx/PrintTargetPDF.h"
+#include "mozilla/gfx/PrintTargetPS.h"
+#include "mozilla/Logging.h"
+
+#include "plstr.h"
+#include "prenv.h" /* for PR_GetEnv */
+
+#include "nsPrintfCString.h"
+#include "nsReadableUtils.h"
+#include "nsStringEnumerator.h"
+#include "nsIServiceManager.h"
+#include "nsThreadUtils.h"
+
+#include "nsPSPrinters.h"
+#include "nsPaperPS.h" /* Paper size list */
+
+#include "nsPrintSettingsGTK.h"
+
+#include "nsIFileStreams.h"
+#include "nsIFile.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+
+#include "mozilla/Preferences.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace mozilla;
+
+using mozilla::gfx::IntSize;
+using mozilla::gfx::PrintTarget;
+using mozilla::gfx::PrintTargetPDF;
+using mozilla::gfx::PrintTargetPS;
+
+static PRLogModuleInfo *
+GetDeviceContextSpecGTKLog()
+{
+ static PRLogModuleInfo *sLog;
+ if (!sLog)
+ sLog = PR_NewLogModule("DeviceContextSpecGTK");
+ return sLog;
+}
+/* Macro to make lines shorter */
+#define DO_PR_DEBUG_LOG(x) MOZ_LOG(GetDeviceContextSpecGTKLog(), mozilla::LogLevel::Debug, x)
+
+//----------------------------------------------------------------------------------
+// The printer data is shared between the PrinterEnumerator and the nsDeviceContextSpecGTK
+// The PrinterEnumerator creates the printer info
+// but the nsDeviceContextSpecGTK cleans it up
+// If it gets created (via the Page Setup Dialog) but the user never prints anything
+// then it will never be delete, so this class takes care of that.
+class GlobalPrinters {
+public:
+ static GlobalPrinters* GetInstance() { return &mGlobalPrinters; }
+ ~GlobalPrinters() { FreeGlobalPrinters(); }
+
+ void FreeGlobalPrinters();
+ nsresult InitializeGlobalPrinters();
+
+ bool PrintersAreAllocated() { return mGlobalPrinterList != nullptr; }
+ uint32_t GetNumPrinters()
+ { return mGlobalPrinterList ? mGlobalPrinterList->Length() : 0; }
+ nsString* GetStringAt(int32_t aInx) { return &mGlobalPrinterList->ElementAt(aInx); }
+ void GetDefaultPrinterName(char16_t **aDefaultPrinterName);
+
+protected:
+ GlobalPrinters() {}
+
+ static GlobalPrinters mGlobalPrinters;
+ static nsTArray<nsString>* mGlobalPrinterList;
+};
+
+//---------------
+// static members
+GlobalPrinters GlobalPrinters::mGlobalPrinters;
+nsTArray<nsString>* GlobalPrinters::mGlobalPrinterList = nullptr;
+//---------------
+
+nsDeviceContextSpecGTK::nsDeviceContextSpecGTK()
+ : mGtkPrintSettings(nullptr)
+ , mGtkPageSetup(nullptr)
+{
+ DO_PR_DEBUG_LOG(("nsDeviceContextSpecGTK::nsDeviceContextSpecGTK()\n"));
+}
+
+nsDeviceContextSpecGTK::~nsDeviceContextSpecGTK()
+{
+ DO_PR_DEBUG_LOG(("nsDeviceContextSpecGTK::~nsDeviceContextSpecGTK()\n"));
+
+ if (mGtkPageSetup) {
+ g_object_unref(mGtkPageSetup);
+ }
+
+ if (mGtkPrintSettings) {
+ g_object_unref(mGtkPrintSettings);
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsDeviceContextSpecGTK,
+ nsIDeviceContextSpec)
+
+already_AddRefed<PrintTarget> nsDeviceContextSpecGTK::MakePrintTarget()
+{
+ double width, height;
+ mPrintSettings->GetEffectivePageSize(&width, &height);
+
+ // convert twips to points
+ width /= TWIPS_PER_POINT_FLOAT;
+ height /= TWIPS_PER_POINT_FLOAT;
+
+ DO_PR_DEBUG_LOG(("\"%s\", %f, %f\n", mPath, width, height));
+ nsresult rv;
+
+ // We shouldn't be attempting to get a surface if we've already got a spool
+ // file.
+ MOZ_ASSERT(!mSpoolFile);
+
+ // Spool file. Use Glib's temporary file function since we're
+ // already dependent on the gtk software stack.
+ gchar *buf;
+ gint fd = g_file_open_tmp("XXXXXX.tmp", &buf, nullptr);
+ if (-1 == fd)
+ return nullptr;
+ close(fd);
+
+ rv = NS_NewNativeLocalFile(nsDependentCString(buf), false,
+ getter_AddRefs(mSpoolFile));
+ if (NS_FAILED(rv)) {
+ unlink(buf);
+ return nullptr;
+ }
+
+ mSpoolName = buf;
+ g_free(buf);
+
+ mSpoolFile->SetPermissions(0600);
+
+ nsCOMPtr<nsIFileOutputStream> stream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
+ rv = stream->Init(mSpoolFile, -1, -1, 0);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ int16_t format;
+ mPrintSettings->GetOutputFormat(&format);
+
+ // Determine the real format with some GTK magic
+ if (format == nsIPrintSettings::kOutputFormatNative) {
+ if (mIsPPreview) {
+ // There is nothing to detect on Print Preview, use PS.
+ format = nsIPrintSettings::kOutputFormatPS;
+ } else {
+ return nullptr;
+ }
+ }
+
+ IntSize size = IntSize::Truncate(width, height);
+
+ if (format == nsIPrintSettings::kOutputFormatPDF) {
+ return PrintTargetPDF::CreateOrNull(stream, size);
+ }
+
+ int32_t orientation;
+ mPrintSettings->GetOrientation(&orientation);
+ return PrintTargetPS::CreateOrNull(stream,
+ size,
+ orientation == nsIPrintSettings::kPortraitOrientation
+ ? PrintTargetPS::PORTRAIT
+ : PrintTargetPS::LANDSCAPE);
+}
+
+/** -------------------------------------------------------
+ * Initialize the nsDeviceContextSpecGTK
+ * @update dc 2/15/98
+ * @update syd 3/2/99
+ */
+NS_IMETHODIMP nsDeviceContextSpecGTK::Init(nsIWidget *aWidget,
+ nsIPrintSettings* aPS,
+ bool aIsPrintPreview)
+{
+ DO_PR_DEBUG_LOG(("nsDeviceContextSpecGTK::Init(aPS=%p)\n", aPS));
+
+ if (gtk_major_version < 2 ||
+ (gtk_major_version == 2 && gtk_minor_version < 10))
+ return NS_ERROR_NOT_AVAILABLE; // I'm so sorry bz
+
+ mPrintSettings = do_QueryInterface(aPS);
+ if (!mPrintSettings)
+ return NS_ERROR_NO_INTERFACE;
+
+ mIsPPreview = aIsPrintPreview;
+
+ // This is only set by embedders
+ bool toFile;
+ aPS->GetPrintToFile(&toFile);
+
+ mToPrinter = !toFile && !aIsPrintPreview;
+
+ mGtkPrintSettings = mPrintSettings->GetGtkPrintSettings();
+ mGtkPageSetup = mPrintSettings->GetGtkPageSetup();
+
+ // This is a horrible workaround for some printer driver bugs that treat custom page sizes different
+ // to standard ones. If our paper object matches one of a standard one, use a standard paper size
+ // object instead. See bug 414314 for more info.
+ GtkPaperSize* geckosHackishPaperSize = gtk_page_setup_get_paper_size(mGtkPageSetup);
+ GtkPaperSize* standardGtkPaperSize = gtk_paper_size_new(gtk_paper_size_get_name(geckosHackishPaperSize));
+
+ mGtkPageSetup = gtk_page_setup_copy(mGtkPageSetup);
+ mGtkPrintSettings = gtk_print_settings_copy(mGtkPrintSettings);
+
+ GtkPaperSize* properPaperSize;
+ if (gtk_paper_size_is_equal(geckosHackishPaperSize, standardGtkPaperSize)) {
+ properPaperSize = standardGtkPaperSize;
+ } else {
+ properPaperSize = geckosHackishPaperSize;
+ }
+ gtk_print_settings_set_paper_size(mGtkPrintSettings, properPaperSize);
+ gtk_page_setup_set_paper_size_and_default_margins(mGtkPageSetup, properPaperSize);
+ gtk_paper_size_free(standardGtkPaperSize);
+
+ return NS_OK;
+}
+
+static void
+#if (MOZ_WIDGET_GTK == 3)
+print_callback(GtkPrintJob *aJob, gpointer aData, const GError *aError) {
+#else
+print_callback(GtkPrintJob *aJob, gpointer aData, GError *aError) {
+#endif
+ g_object_unref(aJob);
+ ((nsIFile*) aData)->Remove(false);
+}
+
+static void
+ns_release_macro(gpointer aData) {
+ nsIFile* spoolFile = (nsIFile*) aData;
+ NS_RELEASE(spoolFile);
+}
+
+/* static */
+gboolean nsDeviceContextSpecGTK::PrinterEnumerator(GtkPrinter *aPrinter,
+ gpointer aData) {
+ nsDeviceContextSpecGTK *spec = (nsDeviceContextSpecGTK*)aData;
+
+ // Find the printer whose name matches the one inside the settings.
+ nsXPIDLString printerName;
+ nsresult rv =
+ spec->mPrintSettings->GetPrinterName(getter_Copies(printerName));
+ if (NS_SUCCEEDED(rv) && printerName) {
+ NS_ConvertUTF16toUTF8 requestedName(printerName);
+ const char* currentName = gtk_printer_get_name(aPrinter);
+ if (requestedName.Equals(currentName)) {
+ spec->mPrintSettings->SetGtkPrinter(aPrinter);
+
+ // Bug 1145916 - attempting to kick off a print job for this printer
+ // during this tick of the event loop will result in the printer backend
+ // misunderstanding what the capabilities of the printer are due to a
+ // GTK bug (https://bugzilla.gnome.org/show_bug.cgi?id=753041). We
+ // sidestep this by deferring the print to the next tick.
+ NS_DispatchToCurrentThread(NewRunnableMethod(spec, &nsDeviceContextSpecGTK::StartPrintJob));
+ return TRUE;
+ }
+ }
+
+ // We haven't found it yet - keep searching...
+ return FALSE;
+}
+
+void nsDeviceContextSpecGTK::StartPrintJob() {
+ GtkPrintJob* job = gtk_print_job_new(mTitle.get(),
+ mPrintSettings->GetGtkPrinter(),
+ mGtkPrintSettings,
+ mGtkPageSetup);
+
+ if (!gtk_print_job_set_source_file(job, mSpoolName.get(), nullptr))
+ return;
+
+ NS_ADDREF(mSpoolFile.get());
+ gtk_print_job_send(job, print_callback, mSpoolFile, ns_release_macro);
+}
+
+void
+nsDeviceContextSpecGTK::EnumeratePrinters()
+{
+ gtk_enumerate_printers(&nsDeviceContextSpecGTK::PrinterEnumerator, this,
+ nullptr, TRUE);
+}
+
+NS_IMETHODIMP
+nsDeviceContextSpecGTK::BeginDocument(const nsAString& aTitle,
+ const nsAString& aPrintToFileName,
+ int32_t aStartPage, int32_t aEndPage)
+{
+ mTitle.Truncate();
+ AppendUTF16toUTF8(aTitle, mTitle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDeviceContextSpecGTK::EndDocument()
+{
+ if (mToPrinter) {
+ // At this point, we might have a GtkPrinter set up in nsPrintSettingsGTK,
+ // or we might not. In the single-process case, we probably will, as this
+ // is populated by the print settings dialog, or set to the default
+ // printer.
+ // In the multi-process case, we proxy the print settings dialog over to
+ // the parent process, and only get the name of the printer back on the
+ // content process side. In that case, we need to enumerate the printers
+ // on the content side, and find a printer with a matching name.
+
+ GtkPrinter* printer = mPrintSettings->GetGtkPrinter();
+ if (printer) {
+ // We have a printer, so we can print right away.
+ StartPrintJob();
+ } else {
+ // We don't have a printer. We have to enumerate the printers and find
+ // one with a matching name.
+ NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsDeviceContextSpecGTK::EnumeratePrinters));
+ }
+ } else {
+ // Handle print-to-file ourselves for the benefit of embedders
+ nsXPIDLString targetPath;
+ nsCOMPtr<nsIFile> destFile;
+ mPrintSettings->GetToFileName(getter_Copies(targetPath));
+
+ nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(targetPath),
+ false, getter_AddRefs(destFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString destLeafName;
+ rv = destFile->GetLeafName(destLeafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> destDir;
+ rv = destFile->GetParent(getter_AddRefs(destDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mSpoolFile->MoveTo(destDir, destLeafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // This is the standard way to get the UNIX umask. Ugh.
+ mode_t mask = umask(0);
+ umask(mask);
+ // If you're not familiar with umasks, they contain the bits of what NOT to set in the permissions
+ // (thats because files and directories have different numbers of bits for their permissions)
+ destFile->SetPermissions(0666 & ~(mask));
+ }
+ return NS_OK;
+}
+
+// Printer Enumerator
+nsPrinterEnumeratorGTK::nsPrinterEnumeratorGTK()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsPrinterEnumeratorGTK, nsIPrinterEnumerator)
+
+NS_IMETHODIMP nsPrinterEnumeratorGTK::GetPrinterNameList(nsIStringEnumerator **aPrinterNameList)
+{
+ NS_ENSURE_ARG_POINTER(aPrinterNameList);
+ *aPrinterNameList = nullptr;
+
+ nsresult rv = GlobalPrinters::GetInstance()->InitializeGlobalPrinters();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ uint32_t numPrinters = GlobalPrinters::GetInstance()->GetNumPrinters();
+ nsTArray<nsString> *printers = new nsTArray<nsString>(numPrinters);
+ if (!printers) {
+ GlobalPrinters::GetInstance()->FreeGlobalPrinters();
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t count = 0;
+ while( count < numPrinters )
+ {
+ printers->AppendElement(*GlobalPrinters::GetInstance()->GetStringAt(count++));
+ }
+ GlobalPrinters::GetInstance()->FreeGlobalPrinters();
+
+ return NS_NewAdoptingStringEnumerator(aPrinterNameList, printers);
+}
+
+NS_IMETHODIMP nsPrinterEnumeratorGTK::GetDefaultPrinterName(char16_t **aDefaultPrinterName)
+{
+ DO_PR_DEBUG_LOG(("nsPrinterEnumeratorGTK::GetDefaultPrinterName()\n"));
+ NS_ENSURE_ARG_POINTER(aDefaultPrinterName);
+
+ GlobalPrinters::GetInstance()->GetDefaultPrinterName(aDefaultPrinterName);
+
+ DO_PR_DEBUG_LOG(("GetDefaultPrinterName(): default printer='%s'.\n", NS_ConvertUTF16toUTF8(*aDefaultPrinterName).get()));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter(const char16_t *aPrinterName, nsIPrintSettings *aPrintSettings)
+{
+ DO_PR_DEBUG_LOG(("nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter()"));
+
+ NS_ENSURE_ARG_POINTER(aPrintSettings);
+
+ /* Set filename */
+ nsAutoCString filename;
+ const char *path;
+
+ if (!(path = PR_GetEnv("PWD")))
+ path = PR_GetEnv("HOME");
+
+ if (path)
+ filename = nsPrintfCString("%s/mozilla.pdf", path);
+ else
+ filename.AssignLiteral("mozilla.pdf");
+
+ DO_PR_DEBUG_LOG(("Setting default filename to '%s'\n", filename.get()));
+ aPrintSettings->SetToFileName(NS_ConvertUTF8toUTF16(filename).get());
+
+ aPrintSettings->SetIsInitializedFromPrinter(true);
+
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+nsresult GlobalPrinters::InitializeGlobalPrinters ()
+{
+ if (PrintersAreAllocated()) {
+ return NS_OK;
+ }
+
+ mGlobalPrinterList = new nsTArray<nsString>();
+
+ nsPSPrinterList psMgr;
+ if (psMgr.Enabled()) {
+ /* Get the list of PostScript-module printers */
+ // XXX: this function is the only user of GetPrinterList
+ // So it may be interesting to convert the nsCStrings
+ // in this function, we would save one loop here
+ nsTArray<nsCString> printerList;
+ psMgr.GetPrinterList(printerList);
+ for (uint32_t i = 0; i < printerList.Length(); i++)
+ {
+ mGlobalPrinterList->AppendElement(NS_ConvertUTF8toUTF16(printerList[i]));
+ }
+ }
+
+ /* If there are no printers available after all checks, return an error */
+ if (!mGlobalPrinterList->Length())
+ {
+ /* Make sure we do not cache an empty printer list */
+ FreeGlobalPrinters();
+
+ return NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE;
+ }
+
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+void GlobalPrinters::FreeGlobalPrinters()
+{
+ if (mGlobalPrinterList) {
+ delete mGlobalPrinterList;
+ mGlobalPrinterList = nullptr;
+ }
+}
+
+void
+GlobalPrinters::GetDefaultPrinterName(char16_t **aDefaultPrinterName)
+{
+ *aDefaultPrinterName = nullptr;
+
+ bool allocate = !GlobalPrinters::GetInstance()->PrintersAreAllocated();
+
+ if (allocate) {
+ nsresult rv = GlobalPrinters::GetInstance()->InitializeGlobalPrinters();
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ }
+ NS_ASSERTION(GlobalPrinters::GetInstance()->PrintersAreAllocated(), "no GlobalPrinters");
+
+ if (GlobalPrinters::GetInstance()->GetNumPrinters() == 0)
+ return;
+
+ *aDefaultPrinterName = ToNewUnicode(*GlobalPrinters::GetInstance()->GetStringAt(0));
+
+ if (allocate) {
+ GlobalPrinters::GetInstance()->FreeGlobalPrinters();
+ }
+}
+