Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsDeviceContextSpecG.h"
7 :
8 : #include "mozilla/gfx/PrintTargetPDF.h"
9 : #include "mozilla/gfx/PrintTargetPS.h"
10 : #include "mozilla/Logging.h"
11 :
12 : #include "plstr.h"
13 : #include "prenv.h" /* for PR_GetEnv */
14 :
15 : #include "nsPrintfCString.h"
16 : #include "nsReadableUtils.h"
17 : #include "nsStringEnumerator.h"
18 : #include "nsIServiceManager.h"
19 : #include "nsThreadUtils.h"
20 :
21 : #include "nsPSPrinters.h"
22 : #include "nsPaperPS.h" /* Paper size list */
23 :
24 : #include "nsPrintSettingsGTK.h"
25 :
26 : #include "nsIFileStreams.h"
27 : #include "nsIFile.h"
28 : #include "nsTArray.h"
29 : #include "nsThreadUtils.h"
30 :
31 : #include "mozilla/Preferences.h"
32 :
33 : #include <unistd.h>
34 : #include <sys/types.h>
35 : #include <sys/stat.h>
36 :
37 : using namespace mozilla;
38 :
39 : using mozilla::gfx::IntSize;
40 : using mozilla::gfx::PrintTarget;
41 : using mozilla::gfx::PrintTargetPDF;
42 : using mozilla::gfx::PrintTargetPS;
43 :
44 : static LazyLogModule sDeviceContextSpecGTKLog("DeviceContextSpecGTK");
45 : /* Macro to make lines shorter */
46 : #define DO_PR_DEBUG_LOG(x) MOZ_LOG(sDeviceContextSpecGTKLog, mozilla::LogLevel::Debug, x)
47 :
48 : //----------------------------------------------------------------------------------
49 : // The printer data is shared between the PrinterEnumerator and the nsDeviceContextSpecGTK
50 : // The PrinterEnumerator creates the printer info
51 : // but the nsDeviceContextSpecGTK cleans it up
52 : // If it gets created (via the Page Setup Dialog) but the user never prints anything
53 : // then it will never be delete, so this class takes care of that.
54 : class GlobalPrinters {
55 : public:
56 0 : static GlobalPrinters* GetInstance() { return &mGlobalPrinters; }
57 0 : ~GlobalPrinters() { FreeGlobalPrinters(); }
58 :
59 : void FreeGlobalPrinters();
60 : nsresult InitializeGlobalPrinters();
61 :
62 0 : bool PrintersAreAllocated() { return mGlobalPrinterList != nullptr; }
63 0 : uint32_t GetNumPrinters()
64 0 : { return mGlobalPrinterList ? mGlobalPrinterList->Length() : 0; }
65 0 : nsString* GetStringAt(int32_t aInx) { return &mGlobalPrinterList->ElementAt(aInx); }
66 : void GetDefaultPrinterName(char16_t **aDefaultPrinterName);
67 :
68 : protected:
69 3 : GlobalPrinters() {}
70 :
71 : static GlobalPrinters mGlobalPrinters;
72 : static nsTArray<nsString>* mGlobalPrinterList;
73 : };
74 :
75 : //---------------
76 : // static members
77 3 : GlobalPrinters GlobalPrinters::mGlobalPrinters;
78 : nsTArray<nsString>* GlobalPrinters::mGlobalPrinterList = nullptr;
79 : //---------------
80 :
81 0 : nsDeviceContextSpecGTK::nsDeviceContextSpecGTK()
82 : : mGtkPrintSettings(nullptr)
83 0 : , mGtkPageSetup(nullptr)
84 : {
85 0 : DO_PR_DEBUG_LOG(("nsDeviceContextSpecGTK::nsDeviceContextSpecGTK()\n"));
86 0 : }
87 :
88 0 : nsDeviceContextSpecGTK::~nsDeviceContextSpecGTK()
89 : {
90 0 : DO_PR_DEBUG_LOG(("nsDeviceContextSpecGTK::~nsDeviceContextSpecGTK()\n"));
91 :
92 0 : if (mGtkPageSetup) {
93 0 : g_object_unref(mGtkPageSetup);
94 : }
95 :
96 0 : if (mGtkPrintSettings) {
97 0 : g_object_unref(mGtkPrintSettings);
98 : }
99 0 : }
100 :
101 0 : NS_IMPL_ISUPPORTS(nsDeviceContextSpecGTK,
102 : nsIDeviceContextSpec)
103 :
104 0 : already_AddRefed<PrintTarget> nsDeviceContextSpecGTK::MakePrintTarget()
105 : {
106 : double width, height;
107 0 : mPrintSettings->GetEffectivePageSize(&width, &height);
108 :
109 : // convert twips to points
110 0 : width /= TWIPS_PER_POINT_FLOAT;
111 0 : height /= TWIPS_PER_POINT_FLOAT;
112 :
113 0 : DO_PR_DEBUG_LOG(("\"%s\", %f, %f\n", mPath, width, height));
114 : nsresult rv;
115 :
116 : // We shouldn't be attempting to get a surface if we've already got a spool
117 : // file.
118 0 : MOZ_ASSERT(!mSpoolFile);
119 :
120 : // Spool file. Use Glib's temporary file function since we're
121 : // already dependent on the gtk software stack.
122 : gchar *buf;
123 0 : gint fd = g_file_open_tmp("XXXXXX.tmp", &buf, nullptr);
124 0 : if (-1 == fd)
125 0 : return nullptr;
126 0 : close(fd);
127 :
128 0 : rv = NS_NewNativeLocalFile(nsDependentCString(buf), false,
129 0 : getter_AddRefs(mSpoolFile));
130 0 : if (NS_FAILED(rv)) {
131 0 : unlink(buf);
132 0 : return nullptr;
133 : }
134 :
135 0 : mSpoolName = buf;
136 0 : g_free(buf);
137 :
138 0 : mSpoolFile->SetPermissions(0600);
139 :
140 0 : nsCOMPtr<nsIFileOutputStream> stream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
141 0 : rv = stream->Init(mSpoolFile, -1, -1, 0);
142 0 : if (NS_FAILED(rv))
143 0 : return nullptr;
144 :
145 : int16_t format;
146 0 : mPrintSettings->GetOutputFormat(&format);
147 :
148 : // Determine the real format with some GTK magic
149 0 : if (format == nsIPrintSettings::kOutputFormatNative) {
150 0 : if (mIsPPreview) {
151 : // There is nothing to detect on Print Preview, use PS.
152 0 : format = nsIPrintSettings::kOutputFormatPS;
153 : } else {
154 0 : return nullptr;
155 : }
156 : }
157 :
158 0 : IntSize size = IntSize::Truncate(width, height);
159 :
160 0 : if (format == nsIPrintSettings::kOutputFormatPDF) {
161 0 : return PrintTargetPDF::CreateOrNull(stream, size);
162 : }
163 :
164 : int32_t orientation;
165 0 : mPrintSettings->GetOrientation(&orientation);
166 0 : return PrintTargetPS::CreateOrNull(stream,
167 : size,
168 0 : orientation == nsIPrintSettings::kPortraitOrientation
169 : ? PrintTargetPS::PORTRAIT
170 0 : : PrintTargetPS::LANDSCAPE);
171 : }
172 :
173 : /** -------------------------------------------------------
174 : * Initialize the nsDeviceContextSpecGTK
175 : * @update dc 2/15/98
176 : * @update syd 3/2/99
177 : */
178 0 : NS_IMETHODIMP nsDeviceContextSpecGTK::Init(nsIWidget *aWidget,
179 : nsIPrintSettings* aPS,
180 : bool aIsPrintPreview)
181 : {
182 0 : DO_PR_DEBUG_LOG(("nsDeviceContextSpecGTK::Init(aPS=%p)\n", aPS));
183 :
184 0 : if (gtk_major_version < 2 ||
185 0 : (gtk_major_version == 2 && gtk_minor_version < 10))
186 0 : return NS_ERROR_NOT_AVAILABLE; // I'm so sorry bz
187 :
188 0 : mPrintSettings = do_QueryInterface(aPS);
189 0 : if (!mPrintSettings)
190 0 : return NS_ERROR_NO_INTERFACE;
191 :
192 0 : mIsPPreview = aIsPrintPreview;
193 :
194 : // This is only set by embedders
195 : bool toFile;
196 0 : aPS->GetPrintToFile(&toFile);
197 :
198 0 : mToPrinter = !toFile && !aIsPrintPreview;
199 :
200 0 : mGtkPrintSettings = mPrintSettings->GetGtkPrintSettings();
201 0 : mGtkPageSetup = mPrintSettings->GetGtkPageSetup();
202 :
203 : // This is a horrible workaround for some printer driver bugs that treat custom page sizes different
204 : // to standard ones. If our paper object matches one of a standard one, use a standard paper size
205 : // object instead. See bug 414314 for more info.
206 0 : GtkPaperSize* geckosHackishPaperSize = gtk_page_setup_get_paper_size(mGtkPageSetup);
207 0 : GtkPaperSize* standardGtkPaperSize = gtk_paper_size_new(gtk_paper_size_get_name(geckosHackishPaperSize));
208 :
209 0 : mGtkPageSetup = gtk_page_setup_copy(mGtkPageSetup);
210 0 : mGtkPrintSettings = gtk_print_settings_copy(mGtkPrintSettings);
211 :
212 : GtkPaperSize* properPaperSize;
213 0 : if (gtk_paper_size_is_equal(geckosHackishPaperSize, standardGtkPaperSize)) {
214 0 : properPaperSize = standardGtkPaperSize;
215 : } else {
216 0 : properPaperSize = geckosHackishPaperSize;
217 : }
218 0 : gtk_print_settings_set_paper_size(mGtkPrintSettings, properPaperSize);
219 0 : gtk_page_setup_set_paper_size_and_default_margins(mGtkPageSetup, properPaperSize);
220 0 : gtk_paper_size_free(standardGtkPaperSize);
221 :
222 0 : return NS_OK;
223 : }
224 :
225 : static void
226 : #if (MOZ_WIDGET_GTK == 3)
227 0 : print_callback(GtkPrintJob *aJob, gpointer aData, const GError *aError) {
228 : #else
229 : print_callback(GtkPrintJob *aJob, gpointer aData, GError *aError) {
230 : #endif
231 0 : g_object_unref(aJob);
232 0 : ((nsIFile*) aData)->Remove(false);
233 0 : }
234 :
235 : static void
236 0 : ns_release_macro(gpointer aData) {
237 0 : nsIFile* spoolFile = (nsIFile*) aData;
238 0 : NS_RELEASE(spoolFile);
239 0 : }
240 :
241 : /* static */
242 0 : gboolean nsDeviceContextSpecGTK::PrinterEnumerator(GtkPrinter *aPrinter,
243 : gpointer aData) {
244 0 : nsDeviceContextSpecGTK *spec = (nsDeviceContextSpecGTK*)aData;
245 :
246 : // Find the printer whose name matches the one inside the settings.
247 0 : nsXPIDLString printerName;
248 : nsresult rv =
249 0 : spec->mPrintSettings->GetPrinterName(getter_Copies(printerName));
250 0 : if (NS_SUCCEEDED(rv) && printerName) {
251 0 : NS_ConvertUTF16toUTF8 requestedName(printerName);
252 0 : const char* currentName = gtk_printer_get_name(aPrinter);
253 0 : if (requestedName.Equals(currentName)) {
254 0 : spec->mPrintSettings->SetGtkPrinter(aPrinter);
255 :
256 : // Bug 1145916 - attempting to kick off a print job for this printer
257 : // during this tick of the event loop will result in the printer backend
258 : // misunderstanding what the capabilities of the printer are due to a
259 : // GTK bug (https://bugzilla.gnome.org/show_bug.cgi?id=753041). We
260 : // sidestep this by deferring the print to the next tick.
261 0 : NS_DispatchToCurrentThread(
262 0 : NewRunnableMethod("nsDeviceContextSpecGTK::StartPrintJob",
263 : spec,
264 0 : &nsDeviceContextSpecGTK::StartPrintJob));
265 0 : return TRUE;
266 : }
267 : }
268 :
269 : // We haven't found it yet - keep searching...
270 0 : return FALSE;
271 : }
272 :
273 0 : void nsDeviceContextSpecGTK::StartPrintJob() {
274 0 : GtkPrintJob* job = gtk_print_job_new(mTitle.get(),
275 : mPrintSettings->GetGtkPrinter(),
276 : mGtkPrintSettings,
277 0 : mGtkPageSetup);
278 :
279 0 : if (!gtk_print_job_set_source_file(job, mSpoolName.get(), nullptr))
280 0 : return;
281 :
282 0 : NS_ADDREF(mSpoolFile.get());
283 0 : gtk_print_job_send(job, print_callback, mSpoolFile, ns_release_macro);
284 : }
285 :
286 : void
287 0 : nsDeviceContextSpecGTK::EnumeratePrinters()
288 : {
289 : gtk_enumerate_printers(&nsDeviceContextSpecGTK::PrinterEnumerator, this,
290 0 : nullptr, TRUE);
291 0 : }
292 :
293 : NS_IMETHODIMP
294 0 : nsDeviceContextSpecGTK::BeginDocument(const nsAString& aTitle,
295 : const nsAString& aPrintToFileName,
296 : int32_t aStartPage, int32_t aEndPage)
297 : {
298 0 : mTitle.Truncate();
299 0 : AppendUTF16toUTF8(aTitle, mTitle);
300 0 : return NS_OK;
301 : }
302 :
303 0 : NS_IMETHODIMP nsDeviceContextSpecGTK::EndDocument()
304 : {
305 0 : if (mToPrinter) {
306 : // At this point, we might have a GtkPrinter set up in nsPrintSettingsGTK,
307 : // or we might not. In the single-process case, we probably will, as this
308 : // is populated by the print settings dialog, or set to the default
309 : // printer.
310 : // In the multi-process case, we proxy the print settings dialog over to
311 : // the parent process, and only get the name of the printer back on the
312 : // content process side. In that case, we need to enumerate the printers
313 : // on the content side, and find a printer with a matching name.
314 :
315 0 : GtkPrinter* printer = mPrintSettings->GetGtkPrinter();
316 0 : if (printer) {
317 : // We have a printer, so we can print right away.
318 0 : StartPrintJob();
319 : } else {
320 : // We don't have a printer. We have to enumerate the printers and find
321 : // one with a matching name.
322 0 : NS_DispatchToCurrentThread(
323 0 : NewRunnableMethod("nsDeviceContextSpecGTK::EnumeratePrinters",
324 : this,
325 0 : &nsDeviceContextSpecGTK::EnumeratePrinters));
326 : }
327 : } else {
328 : // Handle print-to-file ourselves for the benefit of embedders
329 0 : nsXPIDLString targetPath;
330 0 : nsCOMPtr<nsIFile> destFile;
331 0 : mPrintSettings->GetToFileName(getter_Copies(targetPath));
332 :
333 0 : nsresult rv = NS_NewLocalFile(targetPath, false, getter_AddRefs(destFile));
334 0 : NS_ENSURE_SUCCESS(rv, rv);
335 :
336 0 : nsAutoString destLeafName;
337 0 : rv = destFile->GetLeafName(destLeafName);
338 0 : NS_ENSURE_SUCCESS(rv, rv);
339 :
340 0 : nsCOMPtr<nsIFile> destDir;
341 0 : rv = destFile->GetParent(getter_AddRefs(destDir));
342 0 : NS_ENSURE_SUCCESS(rv, rv);
343 :
344 0 : rv = mSpoolFile->MoveTo(destDir, destLeafName);
345 0 : NS_ENSURE_SUCCESS(rv, rv);
346 :
347 : // This is the standard way to get the UNIX umask. Ugh.
348 0 : mode_t mask = umask(0);
349 0 : umask(mask);
350 : // If you're not familiar with umasks, they contain the bits of what NOT to set in the permissions
351 : // (thats because files and directories have different numbers of bits for their permissions)
352 0 : destFile->SetPermissions(0666 & ~(mask));
353 : }
354 0 : return NS_OK;
355 : }
356 :
357 : // Printer Enumerator
358 0 : nsPrinterEnumeratorGTK::nsPrinterEnumeratorGTK()
359 : {
360 0 : }
361 :
362 0 : NS_IMPL_ISUPPORTS(nsPrinterEnumeratorGTK, nsIPrinterEnumerator)
363 :
364 0 : NS_IMETHODIMP nsPrinterEnumeratorGTK::GetPrinterNameList(nsIStringEnumerator **aPrinterNameList)
365 : {
366 0 : NS_ENSURE_ARG_POINTER(aPrinterNameList);
367 0 : *aPrinterNameList = nullptr;
368 :
369 0 : nsresult rv = GlobalPrinters::GetInstance()->InitializeGlobalPrinters();
370 0 : if (NS_FAILED(rv)) {
371 0 : return rv;
372 : }
373 :
374 0 : uint32_t numPrinters = GlobalPrinters::GetInstance()->GetNumPrinters();
375 0 : nsTArray<nsString> *printers = new nsTArray<nsString>(numPrinters);
376 0 : if (!printers) {
377 0 : GlobalPrinters::GetInstance()->FreeGlobalPrinters();
378 0 : return NS_ERROR_OUT_OF_MEMORY;
379 : }
380 :
381 0 : uint32_t count = 0;
382 0 : while( count < numPrinters )
383 : {
384 0 : printers->AppendElement(*GlobalPrinters::GetInstance()->GetStringAt(count++));
385 : }
386 0 : GlobalPrinters::GetInstance()->FreeGlobalPrinters();
387 :
388 0 : return NS_NewAdoptingStringEnumerator(aPrinterNameList, printers);
389 : }
390 :
391 0 : NS_IMETHODIMP nsPrinterEnumeratorGTK::GetDefaultPrinterName(char16_t **aDefaultPrinterName)
392 : {
393 0 : DO_PR_DEBUG_LOG(("nsPrinterEnumeratorGTK::GetDefaultPrinterName()\n"));
394 0 : NS_ENSURE_ARG_POINTER(aDefaultPrinterName);
395 :
396 0 : GlobalPrinters::GetInstance()->GetDefaultPrinterName(aDefaultPrinterName);
397 :
398 0 : DO_PR_DEBUG_LOG(("GetDefaultPrinterName(): default printer='%s'.\n", NS_ConvertUTF16toUTF8(*aDefaultPrinterName).get()));
399 0 : return NS_OK;
400 : }
401 :
402 0 : NS_IMETHODIMP nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter(const char16_t *aPrinterName, nsIPrintSettings *aPrintSettings)
403 : {
404 0 : DO_PR_DEBUG_LOG(("nsPrinterEnumeratorGTK::InitPrintSettingsFromPrinter()"));
405 :
406 0 : NS_ENSURE_ARG_POINTER(aPrintSettings);
407 :
408 : /* Set filename */
409 0 : nsAutoCString filename;
410 : const char *path;
411 :
412 0 : if (!(path = PR_GetEnv("PWD")))
413 0 : path = PR_GetEnv("HOME");
414 :
415 0 : if (path)
416 0 : filename = nsPrintfCString("%s/mozilla.pdf", path);
417 : else
418 0 : filename.AssignLiteral("mozilla.pdf");
419 :
420 0 : DO_PR_DEBUG_LOG(("Setting default filename to '%s'\n", filename.get()));
421 0 : aPrintSettings->SetToFileName(NS_ConvertUTF8toUTF16(filename).get());
422 :
423 0 : aPrintSettings->SetIsInitializedFromPrinter(true);
424 :
425 0 : return NS_OK;
426 : }
427 :
428 : //----------------------------------------------------------------------
429 0 : nsresult GlobalPrinters::InitializeGlobalPrinters ()
430 : {
431 0 : if (PrintersAreAllocated()) {
432 0 : return NS_OK;
433 : }
434 :
435 0 : mGlobalPrinterList = new nsTArray<nsString>();
436 :
437 0 : nsPSPrinterList psMgr;
438 0 : if (psMgr.Enabled()) {
439 : /* Get the list of PostScript-module printers */
440 : // XXX: this function is the only user of GetPrinterList
441 : // So it may be interesting to convert the nsCStrings
442 : // in this function, we would save one loop here
443 0 : nsTArray<nsCString> printerList;
444 0 : psMgr.GetPrinterList(printerList);
445 0 : for (uint32_t i = 0; i < printerList.Length(); i++)
446 : {
447 0 : mGlobalPrinterList->AppendElement(NS_ConvertUTF8toUTF16(printerList[i]));
448 : }
449 : }
450 :
451 : /* If there are no printers available after all checks, return an error */
452 0 : if (!mGlobalPrinterList->Length())
453 : {
454 : /* Make sure we do not cache an empty printer list */
455 0 : FreeGlobalPrinters();
456 :
457 0 : return NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE;
458 : }
459 :
460 0 : return NS_OK;
461 : }
462 :
463 : //----------------------------------------------------------------------
464 0 : void GlobalPrinters::FreeGlobalPrinters()
465 : {
466 0 : if (mGlobalPrinterList) {
467 0 : delete mGlobalPrinterList;
468 0 : mGlobalPrinterList = nullptr;
469 : }
470 0 : }
471 :
472 : void
473 0 : GlobalPrinters::GetDefaultPrinterName(char16_t **aDefaultPrinterName)
474 : {
475 0 : *aDefaultPrinterName = nullptr;
476 :
477 0 : bool allocate = !GlobalPrinters::GetInstance()->PrintersAreAllocated();
478 :
479 0 : if (allocate) {
480 0 : nsresult rv = GlobalPrinters::GetInstance()->InitializeGlobalPrinters();
481 0 : if (NS_FAILED(rv)) {
482 0 : return;
483 : }
484 : }
485 0 : NS_ASSERTION(GlobalPrinters::GetInstance()->PrintersAreAllocated(), "no GlobalPrinters");
486 :
487 0 : if (GlobalPrinters::GetInstance()->GetNumPrinters() == 0)
488 0 : return;
489 :
490 0 : *aDefaultPrinterName = ToNewUnicode(*GlobalPrinters::GetInstance()->GetStringAt(0));
491 :
492 0 : if (allocate) {
493 0 : GlobalPrinters::GetInstance()->FreeGlobalPrinters();
494 : }
495 : }
496 :
|