fltk/src/drivers/Posix/Fl_Posix_Printer_Driver.cxx
2021-02-16 11:08:59 +01:00

335 lines
13 KiB
C++

//
// PostScript priting support for the Fast Light Tool Kit (FLTK).
//
// Copyright 2010-2020 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file. If this
// file is missing or damaged, see the license at:
//
// https://www.fltk.org/COPYING.php
//
// Please see the following page on how to report bugs and issues:
//
// https://www.fltk.org/bugs.php
//
#include <config.h>
#if !defined(FL_NO_PRINT_SUPPORT)
#include <FL/Fl_PostScript.H>
#include "../PostScript/Fl_PostScript_Graphics_Driver.H"
#include <FL/Fl_Printer.H>
#include <FL/fl_ask.H>
#include <src/print_panel.cxx>
/** Support for printing on the Unix/Linux platform */
class Fl_Posix_Printer_Driver : public Fl_PostScript_File_Device {
virtual int begin_job(int pagecount = 0, int *frompage = NULL, int *topage = NULL, char **perr_message=NULL);
};
#if HAVE_DLSYM && HAVE_DLFCN_H
// GTK types
#include <dlfcn.h> // for dlopen et al
#include <unistd.h> // for mkstemp
#include <FL/filename.H>
#include "../X11/Fl_X11_System_Driver.H"
#define GTK_PAPER_NAME_LETTER "na_letter"
#define GTK_RESPONSE_NONE 0
#define GTK_RESPONSE_OK -5
#define GTK_PRINT_PAGES_RANGES 2
class Fl_GTK_Printer_Driver : public Fl_PostScript_File_Device {
public:
typedef int gboolean;
typedef struct _GtkPrintUnixDialog GtkPrintUnixDialog;
typedef struct _GtkDialog GtkDialog;
typedef struct _GtkPrintSettings GtkPrintSettings;
typedef struct _GtkPageSetup GtkPageSetup;
enum GtkPageOrientation {GTK_PAGE_ORIENTATION_PORTRAIT, GTK_PAGE_ORIENTATION_LANDSCAPE};
typedef struct _GtkPaperSize GtkPaperSize;
typedef struct _GtkPrinter GtkPrinter;
typedef struct _GtkPrintJob GtkPrintJob;
typedef struct _GtkWidget GtkWidget;
struct GError;
GtkPrintJob *pjob; // data shared between begin_job() and end_job()
char tmpfilename[50]; // name of temporary PostScript file containing to-be-printed data
virtual int begin_job(int pagecount = 0, int *frompage = NULL, int *topage = NULL, char **perr_message=NULL);
virtual void end_job();
static bool probe_for_GTK();
static void *ptr_gtk; // points to the GTK dynamic lib or NULL
typedef GtkPrintUnixDialog* (*gtk_print_unix_dialog_new_t)(const char*, void*);
typedef int (*gtk_dialog_run_t)(GtkDialog*);
typedef GtkPrintSettings *(*gtk_print_unix_dialog_get_settings_t)(GtkPrintUnixDialog*);
typedef void (*gtk_print_unix_dialog_set_settings_t)(GtkPrintUnixDialog*, GtkPrintSettings*);
typedef GtkPageSetup *(*gtk_print_unix_dialog_get_page_setup_t)(GtkPrintUnixDialog*);
typedef GtkPageOrientation (*gtk_page_setup_get_orientation_t)(GtkPageSetup*);
typedef GtkPaperSize* (*gtk_page_setup_get_paper_size_t)(GtkPageSetup*);
typedef const char * (*gtk_paper_size_get_name_t)(GtkPaperSize*);
typedef GtkPrinter * (*gtk_print_unix_dialog_get_selected_printer_t)(GtkPrintUnixDialog*);
typedef int (*gtk_printer_accepts_ps_t)(GtkPrinter*);
typedef int (*gtk_printer_is_active_t)(GtkPrinter*);
typedef GtkPrintJob *(*gtk_print_job_new_t)(const char *, GtkPrinter *, GtkPrintSettings *, GtkPageSetup *);
typedef void (*gtk_widget_hide_t)(GtkWidget*);
typedef void (*gtk_widget_destroy_t)(GtkWidget*);
typedef gboolean (*gtk_events_pending_t)(void);
typedef void (*gtk_main_iteration_t)(void);
typedef int (*gtk_print_job_set_source_file_t)(GtkPrintJob *job, const char *filename, GError **error);
typedef void (*gtk_print_job_send_t)(GtkPrintJob *, void* , gboolean* , void* );
typedef void (*gtk_print_settings_set_t) (GtkPrintSettings *settings, const char *key, const char *value);
typedef const char * (*gtk_print_settings_get_t) (GtkPrintSettings *settings, const char *key );
typedef int (*gtk_print_settings_get_print_pages_t)(GtkPrintSettings*);
struct GtkPageRange { int start, end; };
typedef GtkPageRange* (*gtk_print_settings_get_page_ranges_t)(GtkPrintSettings*, int*);
typedef void (*g_object_unref_t)(void* object);
typedef void (*gtk_print_unix_dialog_set_embed_page_setup_t)(GtkPrintUnixDialog *dialog, gboolean embed);
typedef const char * (*gtk_check_version_t)(unsigned, unsigned, unsigned);
};
// the CALL_GTK macro produces the source code to call a GTK function given its name
// or to get a pointer to this function :
// CALL_GTK(gtk_my_function) produces ((gtk_my_function_t)dlsym(ptr_gtk, "gtk_my_function"))
#define CALL_GTK(NAME) ((NAME##_t)dlsym(ptr_gtk, #NAME))
void *Fl_GTK_Printer_Driver::ptr_gtk = NULL;
// test wether GTK is available at run-time
bool Fl_GTK_Printer_Driver::probe_for_GTK() {
return Fl_X11_System_Driver::probe_for_GTK(2, 10, &ptr_gtk);
}
int Fl_GTK_Printer_Driver::begin_job(int pagecount, int *firstpage, int *lastpage, char **perr_message) {
enum Fl_Paged_Device::Page_Format format = Fl_Paged_Device::A4;
enum Fl_Paged_Device::Page_Layout layout = Fl_Paged_Device::PORTRAIT ;
GtkPrintUnixDialog *pdialog = CALL_GTK(gtk_print_unix_dialog_new)(Fl_Printer::dialog_title, NULL); //2.10
if (dlsym(ptr_gtk, "gtk_get_major_version") || !CALL_GTK(gtk_check_version)(2, 18, 0))
CALL_GTK(gtk_print_unix_dialog_set_embed_page_setup)(pdialog, true); //2.18
GtkPrintSettings *psettings = CALL_GTK(gtk_print_unix_dialog_get_settings)(pdialog); //2.10
CALL_GTK(gtk_print_settings_set)(psettings, "output-file-format", "ps"); //2.10
char line[FL_PATH_MAX + 20], cwd[FL_PATH_MAX];
sprintf(line, "file://%s/FLTK.ps", fl_getcwd(cwd, FL_PATH_MAX));
CALL_GTK(gtk_print_settings_set)(psettings, "output-uri", line); //2.10
CALL_GTK(gtk_print_unix_dialog_set_settings)(pdialog, psettings); //2.10
CALL_GTK(g_object_unref)(psettings);
int response_id = CALL_GTK(gtk_dialog_run)((GtkDialog*)pdialog);
if (response_id == GTK_RESPONSE_OK) {
GtkPageSetup *psetup = CALL_GTK(gtk_print_unix_dialog_get_page_setup)(pdialog); //2.10
GtkPageOrientation orient = CALL_GTK(gtk_page_setup_get_orientation)(psetup); //2.10
if (orient == GTK_PAGE_ORIENTATION_LANDSCAPE) layout = Fl_Paged_Device::LANDSCAPE;
GtkPaperSize* psize = CALL_GTK(gtk_page_setup_get_paper_size)(psetup); //2.10
const char *pname = CALL_GTK(gtk_paper_size_get_name)(psize); //2.10
if (strcmp(pname, GTK_PAPER_NAME_LETTER) == 0) format = Fl_Paged_Device::LETTER;
GtkPrinter *gprinter = CALL_GTK(gtk_print_unix_dialog_get_selected_printer)(pdialog); //2.10
psettings = CALL_GTK(gtk_print_unix_dialog_get_settings)(pdialog); //2.10
const char* p = CALL_GTK(gtk_print_settings_get)(psettings, "output-uri"); //2.10
bool printing_to_file = (p != NULL);
if (printing_to_file) {
p += 6; // skip "file://" prefix
strcpy(line, p);
int l = strlen(p);
if (strcmp(p+l-4, "/.ps") == 0) {
line[l-3] = 0;
strcat(line, "FLTK.ps");
}
}
if (firstpage && lastpage) {
*firstpage = 1; *lastpage = pagecount;
if (CALL_GTK(gtk_print_settings_get_print_pages)(psettings) == GTK_PRINT_PAGES_RANGES) { // 2.10
int num_ranges;
GtkPageRange *ranges = CALL_GTK(gtk_print_settings_get_page_ranges)(psettings, &num_ranges); //2.10
if (num_ranges > 0) {
*firstpage = ranges[0].start + 1;
*lastpage = ranges[0].end + 1;
free(ranges);
}
}
}
response_id = GTK_RESPONSE_NONE;
if (printing_to_file) {
pjob = NULL;
FILE *output = fopen(line, "w");
if (output) {
Fl_PostScript_File_Device::begin_job(output, 0, format, layout);
response_id = GTK_RESPONSE_OK;
} else {
response_id = GTK_RESPONSE_NONE + GTK_RESPONSE_OK + 1;
if (perr_message) {
*perr_message = new char[strlen(line)+50];
sprintf(*perr_message, "Can't open output file %s", line);
}
}
} else if ( CALL_GTK(gtk_printer_accepts_ps)(gprinter) && //2.10
CALL_GTK(gtk_printer_is_active)(gprinter) ) { // 2.10
strcpy(tmpfilename, "/tmp/FLTKprintjobXXXXXX");
int fd = mkstemp(tmpfilename);
if (fd >= 0) {
FILE *output = fdopen(fd, "w");
Fl_PostScript_File_Device::begin_job(output, 0, format, layout);
pjob = CALL_GTK(gtk_print_job_new)("FLTK print job", gprinter, psettings, psetup); //2.10
response_id = GTK_RESPONSE_OK;
} else {
response_id = GTK_RESPONSE_NONE + GTK_RESPONSE_OK + 1;
if (perr_message) {
*perr_message = new char[strlen(tmpfilename)+50];
sprintf(*perr_message, "Can't create temporary file %s", tmpfilename);
}
}
}
CALL_GTK(g_object_unref)(psettings);
}
CALL_GTK(gtk_widget_hide)((GtkWidget*)pdialog);
gtk_events_pending_t fl_gtk_events_pending = CALL_GTK(gtk_events_pending);
gtk_main_iteration_t fl_gtk_main_iteration = CALL_GTK(gtk_main_iteration);
while (fl_gtk_events_pending()) fl_gtk_main_iteration();
CALL_GTK(gtk_widget_destroy)((GtkWidget*)pdialog);
Fl_Window *first = Fl::first_window();
if (first) {
Fl_Surface_Device::push_current(Fl_Display_Device::display_device());
first->show();
while (Fl::ready()) Fl::check();
Fl_Surface_Device::pop_current();
}
return (response_id == GTK_RESPONSE_OK ? 0 : (response_id == GTK_RESPONSE_NONE ? 1 : 2));
}
static void pJobCompleteFunc(Fl_GTK_Printer_Driver::GtkPrintJob *print_job, Fl_GTK_Printer_Driver::gboolean *user_data, const Fl_GTK_Printer_Driver::GError *error) {
*user_data = true;
}
static void pDestroyNotify(void* data) {}
void Fl_GTK_Printer_Driver::end_job() {
Fl_PostScript_File_Device::end_job();
Fl_PostScript_Graphics_Driver *psgd = driver();
fclose(psgd->output);
if (!pjob) return;
GError *gerr;
gboolean gb = CALL_GTK(gtk_print_job_set_source_file)(pjob, tmpfilename, &gerr); //2.10
if (gb) {
gb = false;
CALL_GTK(gtk_print_job_send)(pjob, (void*)pJobCompleteFunc, &gb, (void*)pDestroyNotify); //2.10
gtk_main_iteration_t fl_gtk_main_iteration = CALL_GTK(gtk_main_iteration);
while (!gb) {
fl_gtk_main_iteration();
}
}
fl_unlink(tmpfilename);
}
#endif // HAVE_DLSYM && HAVE_DLFCN_H
Fl_Paged_Device* Fl_Printer::newPrinterDriver(void)
{
#if HAVE_DLSYM && HAVE_DLFCN_H
static bool gtk = ( Fl::option(Fl::OPTION_PRINTER_USES_GTK) ? Fl_GTK_Printer_Driver::probe_for_GTK() : false);
if (gtk) return new Fl_GTK_Printer_Driver();
#endif
return new Fl_Posix_Printer_Driver();
}
/* Begins a print job. */
int Fl_Posix_Printer_Driver::begin_job(int pages, int *firstpage, int *lastpage, char **perr_message) {
enum Fl_Paged_Device::Page_Format format;
enum Fl_Paged_Device::Page_Layout layout;
// first test version for print dialog
if (!print_panel) make_print_panel();
printing_style style = print_load();
print_selection->deactivate();
print_all->setonly();
print_all->do_callback();
print_from->value("1");
{ char tmp[10]; snprintf(tmp, sizeof(tmp), "%d", pages); print_to->value(tmp); }
print_panel->show(); // this is modal
while (print_panel->shown()) Fl::wait();
if (!print_start) // user clicked cancel
return 1;
// get options
switch (print_page_size->value()) {
case 0:
format = Fl_Paged_Device::LETTER;
break;
case 2:
format = Fl_Paged_Device::LEGAL;
break;
case 3:
format = Fl_Paged_Device::EXECUTIVE;
break;
case 4:
format = Fl_Paged_Device::A3;
break;
case 5:
format = Fl_Paged_Device::A5;
break;
case 6:
format = Fl_Paged_Device::B5;
break;
case 7:
format = Fl_Paged_Device::ENVELOPE;
break;
case 8:
format = Fl_Paged_Device::DLE;
break;
default:
format = Fl_Paged_Device::A4;
}
{ // page range choice
int from = 1, to = pages;
if (print_pages->value()) {
sscanf(print_from->value(), "%d", &from);
sscanf(print_to->value(), "%d", &to);
}
if (from < 1) from = 1;
if (to > pages) to = pages;
if (to < from) to = from;
if (firstpage) *firstpage = from;
if (lastpage) *lastpage = to;
if (pages > 0) pages = to - from + 1;
}
if (print_output_mode[0]->value()) layout = Fl_Paged_Device::PORTRAIT;
else if (print_output_mode[1]->value()) layout = Fl_Paged_Device::LANDSCAPE;
else if (print_output_mode[2]->value()) layout = Fl_Paged_Device::PORTRAIT;
else layout = Fl_Paged_Device::LANDSCAPE;
int print_pipe = print_choice->value(); // 0 = print to file, >0 = printer (pipe)
const char *media = print_page_size->text(print_page_size->value());
const char *printer = (const char *)print_choice->menu()[print_choice->value()].user_data();
if (!print_pipe) printer = "<File>";
if (!print_pipe) // fall back to file printing
return Fl_PostScript_File_Device::begin_job (pages, format, layout);
// Print: pipe the output into the lp command...
char command[1024];
if (style == SystemV) snprintf(command, sizeof(command), "lp -s -d %s -n %d -t '%s' -o media=%s",
printer, print_collate_button->value() ? 1 : (int)(print_copies->value() + 0.5), "FLTK", media);
else snprintf(command, sizeof(command), "lpr -h -P%s -#%d -T FLTK ",
printer, print_collate_button->value() ? 1 : (int)(print_copies->value() + 0.5));
Fl_PostScript_Graphics_Driver *ps = driver();
ps->output = popen(command, "w");
if (!ps->output) {
if (perr_message) {
*perr_message = new char[strlen(command) + 50];
sprintf(*perr_message, "could not run command: %s", command);
}
return 2;
}
ps->close_command(pclose);
Fl_Surface_Device::push_current(this);
return ps->start_postscript(pages, format, layout); // start printing
}
#endif // !defined(FL_NO_PRINT_SUPPORT)