/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * Printer plugin
 * Copyright (C) 1997 Matthias Cramer 
 * (email: cramer@freestone.ch, WWW: http://farpoint.freestone.ch/~cramer)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/* 
   This code is based on the ps plugin, which is copyrighted by
   Peter Kirchgessner (pkirchg@aol.com)
*/
             
/* Event history:
 * V 0.01, MC, 21-Jun-97: Creation
 * V 0.02, MC, 07-Jul-97: Added Printer Name box
 * V 0.03, MC, 08-Jul-97: Added Destination feature
 * V 0.04, MC, 02-Aug-97: Added preview with ghostview
 * V 0.05, MC, 12-Oct-97: Register entry changes to "PostScript Print ... unreleades
 * V 0.051,MC, 30-Oct-97: Playing aroud with the printer list box ... unreleased
 * V 0.06, MC, 09-Nov-97: Printer list box now works, added Makefile and separated from GIMP - tree
 */


/* 
   To do ....
   - Nothing up at the time ... any ideas ?
   - mail me .. :-)
*/


#define VERSION 0.06
#define TITLE "PostScript Print v0.06"
#define AUTHOR "Matthias Cramer <cramer@freestone.net>\0"

static char ident[] = "@(#) GIMP PostScript Print Plugin v0.06 09-Nov-1997";

static char LPRPROGRAM[] = "/usr/bin/lpr -P";
static char GSPROGRAM[] = "/usr/X11R6/bin/ghostview -";

#include <stdio.h>
#include <stdlib.h>
#include "gtk/gtk.h"
#include "libgimp/gimp.h"
#include <time.h>
#include <signal.h>


void      query  (void);
void      run    (char      *name,
		  int        nparams,
		  GParam    *param,
		  int       *nreturn_vals,
		  GParam   **return_vals);


/* Variables and typdefs */

GDrawable *drawable, *mask;


GPlugInInfo PLUG_IN_INFO =
{
  NULL,    /* init_proc */
  NULL,    /* quit_proc */
  query,   /* query_proc */
  run,     /* run_proc */
};


/* The run mode */
static GRunModeType l_run_mode;


/* Print info  */
typedef struct
{
  gdouble x_offset, y_offset; /* Offset to image on page */
  gint rotate;                /* Rotation (0, 90, 180, 270) */
  gint dpi;                   /* Resolution in dpi */
  gchar *file;                /* File name */
  gint dest;                  /* Destination */
  gchar *Printer;             /* Printer name */
} PrintVals;

typedef struct
{
  gint  run;  /*  run  */
} PrintInterface;


static PrintVals printvals =
{
  5.0, 5.0,       /* Offset */
  90,             /* Rotate */
  300,            /* Resolution */
  "default.ps",   /* Default File */
  0,              /* Destination */
  "",             /* Printer name */
};


static PrintInterface printint =
{
  FALSE     /* run */
};


typedef struct
{
  GtkWidget *dialog;
  GtkWidget *entry[4];
  int keep_ratio;
  int unit[2];
  int rot[4];
  int dest[3];
  int pmenu;
  int p[20];
} PrintDialogVals;


          
typedef struct _MessageBox MessageBox;

struct _MessageBox
{
  GtkWidget  *mbox;
  GtkCallback callback;
  gpointer    data;
};


typedef char ty_PrinterArray[30];
ty_PrinterArray PrinterArray[20];

gint	num_of_printers;

/* ------------- Function Prototypes ----------------------- */



static gint   print_dialog (void);
static gint   print_image  (char *filename,
                           gint32  image_ID, 
                           gint32  drawable_ID);
static void   show_message (char *);
static void   check_print_vals (void);
static gint   print_image  (char *filename,
                            gint32  image_ID,
                            gint32  drawable_ID);
static void   print_close_callback      (GtkWidget *widget,
                                        gpointer   data);
static void   print_callback            (GtkWidget *widget,
                                        gpointer   data);
static void   print_toggle_update       (GtkWidget *widget,
                                        gpointer   data);
static void   print_menu_update         (GtkWidget *widget,
                                        gpointer   data);
static char  *ftoa (char *format, double r);                                        
static void print_ps_header (FILE *ofp);
static void print_ps_transform (FILE *ofp,
                               int width,
                               int height,
                               int bpp);
static void print_ps_trailer (FILE *ofp);
static gint print_gray  (FILE *ofp,
                        gint32 image_ID,
                        gint32 drawable_ID,
                        const PrintVals *printopt);
static gint print_index (FILE *ofp,
                        gint32 image_ID,
                        gint32 drawable_ID,
                        const PrintVals *printopt);
static gint print_rgb   (FILE *ofp,
                        gint32 image_ID,
                        gint32 drawable_ID,
                        const PrintVals *printopt);
                        
static gint printcap_parse(void);

MAIN();


void
query ()
{
  GParamDef args[] =
  {
    { PARAM_INT32, "run_mode", "Interactive, non-interactive" },
    { PARAM_IMAGE, "image", "Input image (used for indexed images)" },
    { PARAM_DRAWABLE, "drawable", "Input drawable" },
  };
   GParamDef *return_vals = NULL;
   int nargs = sizeof (args) / sizeof (args[0]);
   int nreturn_vals = 0;

  gimp_install_procedure ("PostScript Print ...",
			  "Print image in varios resolutions to a PostScript Printer",
			  "GIMP is cool !!", 
			  "Matthias Cramer (cramer@freestone.ch)",
			  "Matthias Cramer (cramer@freestone.net)",
			  "12th October 1997",
			  "<Image>/File/PostScript Print ...",
			  "RGB,INDEXED,GRAY",
			  PROC_PLUG_IN,
			  nargs, nreturn_vals,
			  args, return_vals);
}

/********************************STANDARD RUN*************************/
void
run (char    *name,
     int      nparams,
     GParam  *param,
     int     *nreturn_vals,
     GParam **return_vals)
{
     static GParam values[2];
     GRunModeType run_mode;
     GStatusType status = STATUS_SUCCESS;
           
     l_run_mode = run_mode = param[0].data.d_int32;
              
     *nreturn_vals = 1;
     *return_vals = values;
     values[0].type = PARAM_STATUS;
     values[0].data.d_status = STATUS_CALLING_ERROR;
     
     signal(SIGSEGV, SIG_DFL);

     switch (run_mode)
      {
        case RUN_INTERACTIVE:
          /*  Possibly retrieve data  */
          gimp_get_data ("Print", &printvals);

          /*  First acquire information with a dialog  */
          if (! print_dialog ())
            return;
          break;

        case RUN_NONINTERACTIVE:
          /*  Make sure all the arguments are there!  */
          if (nparams != 11)
          {
            status = STATUS_CALLING_ERROR;
          }
          else
          {
            printvals.x_offset = param[5].data.d_float;
            printvals.y_offset = param[6].data.d_float;
            printvals.rotate = param[7].data.d_int32;
            printvals.dpi = param[8].data.d_int32;
            printvals.file = param[9].data.d_string;
            printvals.dest = param[10].data.d_int32;
            printvals.Printer = param[11].data.d_string;
          }
          break;

        case RUN_WITH_LAST_VALS:
          /*  Possibly retrieve data  */
          gimp_get_data ("Print", &printvals);
          break;

        default:
          break;
      }

      if (status == STATUS_SUCCESS)
      {
        check_print_vals ();
        if (print_image (param[3].data.d_string, param[1].data.d_int32,
                        param[2].data.d_int32))
        {
          /*  Store printvals data  */
          gimp_set_data ("Print", &printvals, sizeof (PrintVals));
        }
        else
        {
          status = STATUS_EXECUTION_ERROR;
        }
      }
      values[0].data.d_status = status;     
                        
}


/* Check (and correct) the print values printvals */
static void
check_print_vals (void)

{int i;

 i = printvals.rotate;
 if ((i != 0) && (i != 90) && (i != 180) && (i != 270))
   printvals.rotate = 90;
}

/* Convert float to ascii for use in labels (cuts off trailing blanks). */
/* The pointer returned is only valid up to the next call of the function. */
static char *ftoa (char *format,
                   double r)

{static char buffer[32];
 register int n;

 sprintf (buffer, format, r);
 n = strlen (buffer)-1;
 while ((n >= 0) && (buffer[n] == ' '))
   buffer[n--] = '\0';
 return (buffer);
}


/* The Print Dialog */

static gint print_dialog (void)
{
  PrintDialogVals *vals;
  GtkWidget *button;
  GtkWidget *toggle;
  GtkWidget *frame;
  GtkWidget *vbox;  
  GtkWidget *label;
  GtkWidget *table;
  GtkWidget *printer_main_menu;
  GtkWidget *printer_menu;
  GtkWidget *menuitem;
  GSList *group;
  GSList *group1;
  
  gchar **argv;
  gint argc;
  static char *label_text[] = { "Resolution [dpi] :", "X-offset [mm] :", "Y-offset [mm] :" };
  static char *radio_text[] = { "0", "90", "180", "270" };
  int j;
  double rdata;

  argc = 1;
  argv = g_new (gchar *, 1);
  argv[0] = g_strdup ("print");

  gtk_init (&argc, &argv);

  vals = g_malloc (sizeof (*vals));

  vals->dialog = gtk_dialog_new ();
  gtk_window_set_title (GTK_WINDOW (vals->dialog), TITLE);
  gtk_window_position (GTK_WINDOW (vals->dialog), GTK_WIN_POS_MOUSE);
  gtk_signal_connect (GTK_OBJECT (vals->dialog), "destroy",
                      (GtkSignalFunc) print_close_callback,
                      NULL);

  /*  Action area  */
  /*  Print Button */
  button = gtk_button_new_with_label ("Start Printing");
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
                      (GtkSignalFunc) print_callback,
                      vals);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (vals->dialog)->action_area), button,
                      TRUE, TRUE, 0);
  gtk_widget_grab_default (button);
  gtk_widget_show (button);

  /* Cancel Button */

  button = gtk_button_new_with_label ("Cancel");
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                             (GtkSignalFunc) gtk_widget_destroy,
                             GTK_OBJECT (vals->dialog));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (vals->dialog)->action_area), button,
                      TRUE, TRUE, 0);
  gtk_widget_show (button);

  /* Image Resolution and offsets */
  frame = gtk_frame_new ("Resolution & Offset");
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_container_border_width (GTK_CONTAINER (frame), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (vals->dialog)->vbox), frame,
                      FALSE, TRUE, 0);
  vbox = gtk_vbox_new (FALSE, 5);
  gtk_container_border_width (GTK_CONTAINER (vbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), vbox);

  /* Resolution/X-/Y-offset labels */
  table = gtk_table_new (3, 2, FALSE);
  gtk_table_set_row_spacings (GTK_TABLE (table), 5);
  gtk_table_set_col_spacings (GTK_TABLE (table), 5);
  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
  gtk_widget_show (table);

  /* Fill in labels */

  for (j = 0; j < 3; j++)
  {
    label = gtk_label_new (label_text[j]);
    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
    gtk_table_attach (GTK_TABLE (table), label, 0, 1, j, j+1,
                      GTK_FILL, GTK_FILL, 0, 0);
    gtk_widget_show (label);
  }

  /* Resolution/X-off/Y-off Entries */
  for (j = 0; j < 3; j++)
  {
    vals->entry[j] = gtk_entry_new ();
    gtk_widget_set_usize (vals->entry[j], 100, 0);
    if      (j == 0) rdata = printvals.dpi;
      else if (j == 1) rdata = printvals.x_offset;
            else 	     rdata = printvals.y_offset;
    if (j == 0) gtk_entry_set_text (GTK_ENTRY (vals->entry[j]), ftoa ("%-8.0f", rdata));
      else gtk_entry_set_text (GTK_ENTRY (vals->entry[j]), ftoa ("%-8.2f", rdata));
    gtk_table_attach (GTK_TABLE (table), vals->entry[j], 1, 2, j, j+1,
                      GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
    gtk_widget_show (vals->entry[j]);
  }

  gtk_widget_show (vbox);
  gtk_widget_show (frame);

  /* Rotation */
  frame = gtk_frame_new ("Rotation");
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_container_border_width (GTK_CONTAINER (frame), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (vals->dialog)->vbox), frame,
                      FALSE, TRUE, 0);
  vbox = gtk_vbox_new (FALSE, 5);
  gtk_container_border_width (GTK_CONTAINER (vbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), vbox);

  group = NULL;
  for (j = 0; j < 4; j++)
  {
    toggle = gtk_radio_button_new_with_label (group, radio_text[j]);
    group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
    gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
    vals->rot[j] = (printvals.rotate == j*90);
    gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
                        (GtkSignalFunc) print_toggle_update,
                        &(vals->rot[j]));
    gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle),
                                 vals->rot[j]);
    gtk_widget_show (toggle);
  }

  gtk_widget_show (vbox);
  gtk_widget_show (frame);
  
  
  /* Printer- or Filename input box */
  
  frame = gtk_frame_new ("Destination");
  gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
  gtk_container_border_width(GTK_CONTAINER (frame),10);
  gtk_box_pack_start(GTK_BOX (GTK_DIALOG (vals->dialog)->vbox), frame,
                     FALSE, TRUE, 0);
                     
  vbox = gtk_vbox_new(6, FALSE);
  gtk_container_border_width (GTK_CONTAINER (vbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), vbox);
  
  label = gtk_label_new ("Output to:");
  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  group1 = NULL;
  
  /* -- Printer Radio Button -- */

  toggle = gtk_radio_button_new_with_label (group1, "Printer");
  group1 = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  vals->dest[0] = (printvals.dest == 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
                      (GtkSignalFunc) print_toggle_update,
                      &(vals->dest[0]));
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle),
                               vals->dest[0]);
  gtk_widget_show (toggle);
  
  /* -- File Radio Button -- */   
    
  toggle = gtk_radio_button_new_with_label (group1, "File");
  group1 = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  vals->dest[1] = (printvals.dest == 1);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
                      (GtkSignalFunc) print_toggle_update,
                      &(vals->dest[1]));
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle),
                               vals->dest[1]);
  gtk_widget_show (toggle);
  
  /* -- Preview Radio Button -- */
  
  toggle = gtk_radio_button_new_with_label (group1, "Preview");
  group1 = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  vals->dest[2] = (printvals.dest == 2);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
                      (GtkSignalFunc) print_toggle_update,
                      &(vals->dest[2]));
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle),
                               vals->dest[2]);
  gtk_widget_show (toggle);

  /* -- Filename Input Box -- */
  /* --- Label --- */

  label = gtk_label_new ("Filename:");
  gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);
  
  /* --- Text Box --- */
  
  vals->entry[3] = gtk_entry_new ();
  gtk_widget_set_usize (vals->entry[3], 100, 0);
  gtk_entry_set_text (GTK_ENTRY (vals->entry[3]), printvals.file);
  gtk_box_pack_start (GTK_BOX (vbox), vals->entry[3], FALSE, FALSE, 0);
  gtk_widget_show (vals->entry[3]);
  
  /* -- Printer Choose Box -- */  
  /* --- Label --- */
  
  label = gtk_label_new ("Printer:");
  gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  /* --- Choose Box --- */
/* --- */

  printer_menu = gtk_menu_new ();
  
  num_of_printers = printcap_parse();

  #ifdef DEBUG
  fprintf(stderr, "\nNumber of Printers : %d\n", num_of_printers);
  #endif

  for (j=0; j<num_of_printers; j++) {
  
     menuitem = gtk_menu_item_new_with_label(PrinterArray[j]);

     #ifdef DEBUG
     fprintf(stderr, "Adding Printer %s\n",PrinterArray[j]);
     #endif

     gtk_menu_append (GTK_MENU (printer_menu), menuitem);
     gtk_widget_show (menuitem);
     gtk_signal_connect (GTK_OBJECT (menuitem), "activate",
                     (GtkSignalFunc) print_menu_update, &PrinterArray[j]);
  }
  
  gtk_menu_set_active (GTK_MENU (printer_menu), 0);
  
  printer_main_menu = gtk_option_menu_new ();
  gtk_box_pack_start (GTK_BOX (vbox), printer_main_menu, TRUE, TRUE, 0);
  gtk_widget_show (printer_main_menu);
  gtk_option_menu_set_menu (GTK_OPTION_MENU (printer_main_menu), printer_menu);

/* set Printer to printer 1 in list, so that it allways has an value */

  printvals.Printer=PrinterArray[0];
  
/* --- */

  gtk_widget_show (vbox);
  gtk_widget_show (frame);

  gtk_widget_show (vals->dialog);

  gtk_main ();
  gdk_flush ();

  g_free (vals);

  return printint.run;
}


static void
print_close_callback (GtkWidget *widget,
                     gpointer   data)

{
  gtk_main_quit ();
}


static void
print_toggle_update (GtkWidget *widget,
                    gpointer   data)

{
  int *toggle_val;

  toggle_val = (int *) data;

  *toggle_val = ((GTK_TOGGLE_BUTTON (widget)->active) != 0);
}

static void
print_menu_update (GtkWidget *widget,
                    gpointer   data)

{
  char *printernam;

  printernam = (char *) data;

#ifdef DEBUG
  fprintf (stderr, "Printer    --> %s\n", printernam);  
#endif
  
  printvals.Printer = printernam;
}


static void
print_callback (GtkWidget *widget,
                  gpointer   data)

{PrintDialogVals *vals = (PrintDialogVals *)data;
 double r;
 int k;
/* GtkWidget *menu_item; */

  /* Read Resolution */
  k = sscanf (gtk_entry_get_text (GTK_ENTRY (vals->entry[0])), "%lf", &r);
  if (k == 1) printvals.dpi = r;

  /* Read x-offset */
  k = sscanf (gtk_entry_get_text (GTK_ENTRY (vals->entry[1])), "%lf", &r);
  if (k == 1) printvals.x_offset = r;
  
  /* Read y-offset */
  k = sscanf (gtk_entry_get_text (GTK_ENTRY (vals->entry[2])), "%lf", &r);
  if (k == 1) printvals.y_offset = r;

  /* Read rotation */
  if (vals->rot[1] == 1) printvals.rotate = 90;
  else if (vals->rot[2] == 1) printvals.rotate = 180;
  else if (vals->rot[3] == 1) printvals.rotate = 270;
  else printvals.rotate = 0;

  /* Read Destination */
  if (vals->dest[0] == 1) printvals.dest = 0;
  else if (vals->dest[1] == 1) printvals.dest = 1;
  else printvals.dest = 2;

  /* Read Filename */
  printvals.file = g_strdup(gtk_entry_get_text (GTK_ENTRY (vals->entry[3])));

  /* Read Printer Name */

  /* Printername is allways up to date (from function print_menu_update() ) */

  fprintf (stderr, "Printer is: %s \n", printvals.Printer);

  printint.run = TRUE;
  gtk_widget_destroy (GTK_WIDGET (vals->dialog));
}

/* Print the image */

static gint
print_image (char *filename,
            gint32  image_ID,
            gint32  drawable_ID)

{
  FILE* ofp;
  GDrawableType drawable_type;
  gint retval=0;
  char *temp = ident; /* Just to satisfy lint/gcc */
  char cmd[50];
  
  drawable_type = gimp_drawable_type (drawable_ID);

  /*  Make sure we're not printing an image with an alpha channel  */
  if (gimp_drawable_has_alpha (drawable_ID))
  {
    show_message ("Print cannot handle images with alpha channels");
    return FALSE;
  }


  if ((printvals.dest == 0) || (printvals.dest == 2)) 
  {
    if (printvals.dest == 0) 
    {
      sprintf(cmd,"%s%s", LPRPROGRAM, printvals.Printer); 
    } else {
      sprintf(cmd,"%s", GSPROGRAM); 
    }
  
    /* Open the output file. */
    ofp = popen (cmd, "w");
  } 
  else ofp = fopen (printvals.file, "w");


  if (!ofp)
  {
    show_message ("cant open file for writing");
    return (FALSE);
  }

  fflush(ofp);
  
  temp = g_malloc (strlen (printvals.file) + 13);
  
  sprintf (temp, "Printing to %s:", printvals.file);
  gimp_progress_init (temp);
  g_free (temp);

  print_ps_header (ofp);

  
  if (drawable_type == GRAY_IMAGE)
    retval = print_gray (ofp,image_ID, drawable_ID, &printvals);
  else if (drawable_type == INDEXED_IMAGE)
    retval = print_index (ofp,image_ID, drawable_ID, &printvals);
  else if (drawable_type == RGB_IMAGE)
    retval = print_rgb (ofp,image_ID, drawable_ID, &printvals);

  print_ps_trailer (ofp);
 
  fflush(ofp);

  if ((printvals.dest == 0) || (printvals.dest == 2)) pclose (ofp);
  else fclose(ofp);

  return (retval);
 
}


/* Write out the PostScript file header */
static void print_ps_header (FILE *ofp)

{time_t cutime = time (NULL);

  fprintf (ofp, "%%!PS-Adobe-3.0\n");
  fprintf (ofp, "%%%%Creator: GIMP PostScriptPrint plugin V %4.2f by %s\n", VERSION, AUTHOR);
  fprintf (ofp, "%%%%Title: Output from printer plugin\n");
  fprintf (ofp, "%%%%CreationDate: %s", ctime (&cutime));
  fprintf (ofp, "%%%%DocumentData: Clean7Bit\n");
  fprintf (ofp, "%%%%Pages: 1\n");
}


/* Write out transformation for image */
static void
print_ps_transform (FILE *ofp,
                   int width,
                   int height,
                   int bpp)

{double x_offset, y_offset, x_size, y_size;
 double width_inch, height_inch;
 double dx=1, dy=1;
 int xtrans, ytrans;

  x_offset = printvals.x_offset / 25.4;
  y_offset = printvals.y_offset / 25.4;

  width_inch = width / printvals.dpi;
  height_inch = height / printvals.dpi;

  if ((printvals.rotate == 0) || (printvals.rotate == 180))
    { x_size = width_inch; y_size = height_inch; }
  else
    { y_size = width_inch; x_size = height_inch; }


  fprintf (ofp, "%%%%BoundingBox: %d %d %d %d\n",(int)(x_offset*72.0),
           (int)(y_offset*72.0), (int)((x_offset+x_size)*72.0)+1,
           (int)((y_offset+y_size)*72.0)+1);

  fprintf (ofp, "%%%%EndComments\n");

  fprintf (ofp, "%%%%BeginProlog\n");
  fprintf (ofp, "%% Use own dictionary to avoid conflicts\n");
  fprintf (ofp, "5 dict begin\n");
  fprintf (ofp, "%%%%EndProlog\n");
  fprintf (ofp, "%%%%Page: 1 1\n");
  fprintf (ofp, "%% Translate for offset\n");
  fprintf (ofp, "%f %f translate\n", x_offset*72.0, y_offset*72.0);

  /* Calculate translation to startpoint of first scanline */
  switch (printvals.rotate)
  {
    case   0: dx = 0.0; dy = y_size*72.0;
              break;
    case  90: dx = dy = 0.0;
              break;
    case 180: dx = x_size*72.0; dy = 0.0; break;
    case 270: dx = x_size*72.0; dy = y_size*72.0; break;
  }
  
  if ((dx != 0.0) || (dy != 0.0))
    fprintf (ofp, "%% Translate to begin of first scanline\n%f %f translate\n",
             dx, dy);
  if (printvals.rotate)
    fprintf (ofp, "%d rotate\n", (int)printvals.rotate);
    fprintf (ofp, "%f %f scale\n", 72.0*width_inch, -72.0*height_inch);

  /* Write the PostScript procedures to read the image */
  fprintf (ofp, "%% Variable to keep one line of raster data\n");
  fprintf (ofp, "/scanline %d %d mul string def\n", width, bpp);
  fprintf (ofp, "%% Image geometry\n%d %d 8\n", width, height);
  fprintf (ofp, "%% Transformation matrix\n");
  xtrans = ytrans = 0;
  fprintf (ofp, "[ %d 0 0 %d %d %d ]\n", width, height, xtrans, ytrans);
}


static void print_ps_trailer (FILE *ofp)

{
  fprintf (ofp, "%%Trailer\n");
  fprintf (ofp, "end\n%%EOF\n");
}


static gint
print_gray  (FILE *ofp,
            gint32 image_ID,
            gint32 drawable_ID,
            const PrintVals *printopt)

{ int height, width, i, j;
  int tile_height;
  unsigned char *data, *src;
  GPixelRgn pixel_rgn;
  GDrawable *drawable;
  GDrawableType drawable_type;
  static char *hex = "0123456789abcdef";

  drawable = gimp_drawable_get (drawable_ID);
  drawable_type = gimp_drawable_type (drawable_ID);
  width = drawable->width;
  height = drawable->height;
  tile_height = gimp_tile_height ();
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);

  /* allocate a buffer for retrieving information from the pixel region  */
  src = data = (unsigned char *)g_malloc (tile_height * width * drawable->bpp);

  /* Set up transformation in PostScript */
  print_ps_transform (ofp, width, height, 1);

  /* Write read image procedure */
  fprintf (ofp, "{ currentfile scanline readhexstring pop }\n");
  fprintf (ofp, "image\n");

#define GET_GRAY_TILE(begin) \
  {int scan_lines; \
    scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
    gimp_pixel_rgn_get_rect (&pixel_rgn, begin, 0, i, width, scan_lines); \
    src = begin; }

  for (i = 0; i < height; i++)
  {
    if ((i % tile_height) == 0) GET_GRAY_TILE (data); /* Get more data */
    for (j = 0; j < width; j++)
    {
      putc (hex[(*src) >> 4], ofp);
      putc (hex[(*(src++)) & 0x0f], ofp);
      if (((j+1) % 39) == 0) putc ('\n', ofp);
    }
    putc ('\n', ofp);
    if ((i % 20) == 0)
      gimp_progress_update ((double) i / (double) height);
  }
  fprintf (ofp, "showpage\n");
  g_free (data);

  gimp_drawable_detach (drawable);

  if (ferror (ofp))
  {
    show_message ("write error occured");
    return (FALSE);
  }
  return (TRUE);
#undef GET_GRAY_TILE
}


static gint
print_index (FILE *ofp,
            gint32 image_ID,
            gint32 drawable_ID,
            const PrintVals *printopt)

{ int height, width, i, j;
  int ncols;
  int tile_height;
  unsigned char *cmap;
  unsigned char *data, *src;
  char coltab[256*6], *ct;
  GPixelRgn pixel_rgn;
  GDrawable *drawable;
  GDrawableType drawable_type;
  static char *hex = "0123456789abcdef";
  static char *background = "000000";

  drawable = gimp_drawable_get (drawable_ID);
  drawable_type = gimp_drawable_type (drawable_ID);
  width = drawable->width;
  height = drawable->height;
  tile_height = gimp_tile_height ();
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);

  /* allocate a buffer for retrieving information from the pixel region  */
  src = data = (unsigned char *)g_malloc (tile_height * width * drawable->bpp);

  cmap = gimp_image_get_cmap (image_ID, &ncols);

  ct = coltab;
  for (j = 0; j < 256; j++)
  {
    if (j >= ncols)
    {
      memcpy (ct, background, 6);
      ct += 6;
    }
    else
    {
      *(ct++) = (unsigned char)hex[(*cmap) >> 4];
      *(ct++) = (unsigned char)hex[(*(cmap++)) & 0x0f];
      *(ct++) = (unsigned char)hex[(*cmap) >> 4];
      *(ct++) = (unsigned char)hex[(*(cmap++)) & 0x0f];
      *(ct++) = (unsigned char)hex[(*cmap) >> 4];		
      *(ct++) = (unsigned char)hex[(*(cmap++)) & 0x0f];
    }
  }

  /* Set up transformation in PostScript */
  print_ps_transform (ofp, width, height, 3);

  /* Write read image procedure */
  fprintf (ofp, "{ currentfile scanline readhexstring pop } false 3\n");
  fprintf (ofp, "colorimage\n");

#define GET_INDEX_TILE(begin) \
  {int scan_lines; \
    scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
    gimp_pixel_rgn_get_rect (&pixel_rgn, begin, 0, i, width, scan_lines); \
    src = begin; }

  for (i = 0; i < height; i++)
  {
    if ((i % tile_height) == 0) GET_INDEX_TILE (data); /* Get more data */
    for (j = 0; j < width; j++)
    {
      fwrite (coltab+(*(src++))*6, 6, 1, ofp);
      if (((j+1) % 13) == 0) putc ('\n', ofp);
    }
    putc ('\n', ofp);
    if ((i % 20) == 0)
      gimp_progress_update ((double) i / (double) height);
  }
  fprintf (ofp, "showpage\n");

  g_free (data);

  gimp_drawable_detach (drawable);

  if (ferror (ofp))
  {
    show_message ("write error occured");
    return (FALSE);
  }
  return (TRUE);
#undef GET_INDEX_TILE
}


static gint
print_rgb (FILE *ofp,
          gint32 image_ID,
          gint32 drawable_ID,
          const PrintVals *printopt)

{
  int height, width, tile_height;
  int i, j;
  unsigned char *data, *src;
  GPixelRgn pixel_rgn;
  GDrawable *drawable;
  GDrawableType drawable_type;
  static char *hex = "0123456789abcdef";

  drawable = gimp_drawable_get (drawable_ID);
  drawable_type = gimp_drawable_type (drawable_ID);
  width = drawable->width;
  height = drawable->height;
  tile_height = gimp_tile_height ();
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);

  /* allocate a buffer for retrieving information from the pixel region  */
  src = data = (unsigned char *)g_malloc (tile_height * width * drawable->bpp);

  /* Set up transformation in PostScript */
  print_ps_transform (ofp, width, height, 3);

  /* Write read image procedure */
  fprintf (ofp, "{ currentfile scanline readhexstring pop } false 3\n");
  fprintf (ofp, "colorimage\n");

/* Macro GET_RGB_TILE */

#define GET_RGB_TILE(begin) \
  {int scan_lines; \
    scan_lines = (i+tile_height-1 < height) ? tile_height : (height-i); \
    gimp_pixel_rgn_get_rect (&pixel_rgn, begin, 0, i, width, scan_lines); \
    src = begin; }

/* End Macro */

  for (i = 0; i < height; i++)
  {
    if ((i % tile_height) == 0) GET_RGB_TILE (data); /* Get more data */
    for (j = 0; j < width; j++)
    {
      putc (hex[(*src) >> 4], ofp);        /* Red */
      putc (hex[(*(src++)) & 0x0f], ofp);
      putc (hex[(*src) >> 4], ofp);        /* Green */
      putc (hex[(*(src++)) & 0x0f], ofp);
      putc (hex[(*src) >> 4], ofp);        /* Blue */
      putc (hex[(*(src++)) & 0x0f], ofp);
      if (((j+1) % 13) == 0) putc ('\n', ofp);
    }
    putc ('\n', ofp);
    if ((i % 20) == 0)
      gimp_progress_update ((double) i / (double) height);
  }
  fprintf (ofp, "showpage\n");
  g_free (data);

  gimp_drawable_detach (drawable);

  if (ferror (ofp))
  {
    show_message ("write error occured");
    return (FALSE);
  }
  return (TRUE);
#undef GET_RGB_TILE
}


static gint printcap_parse(void)
{
  FILE *printcap;
  gint i,j;
  char line[90];
  
  
  printcap=fopen("/etc/printcap", "r");
  #ifdef DEBUG
  show_message("Parsing Printcap File ...");
  #endif
  if (printcap == NULL) {
    show_message("Can\'t open /etc/printcap");
    exit(0); 
  }
  j=0;
  
  while (fgets(line, 80, printcap)) {

    #ifdef DEBUG
    fprintf (stderr, ".");
    #endif
    
    if (line[0] != 35 && line[0] != 32 && line[0] != 58 && line[0] != 9 &&
        line[0] != 13 && line[0] != 10 ) {
      sscanf(line, "%s", PrinterArray[j]);
      for (i=0; i<=strlen(PrinterArray[j]); i++) {
        if (PrinterArray[j][i] == 58 || PrinterArray[j][i] == 124) {
          PrinterArray[j][i] = 0; 
        }
      }
      #ifdef DEBUG
      fprintf (stderr, "+ %s\n",line);
      #endif
      j++;
    }
  }
  fclose(printcap);
  #ifdef DEBUG
  fprintf(stderr, "\nFound %d printers\n", j);
  #endif
  return(j);
}


/* Show a message. */
static void show_message (char *message)

{
#ifdef Simple_Message_Box_Available
 /* If there would be a simple message box like the one */
 /* used in ../app/interface.h, I would like to use it. */
 if (l_run_mode == RUN_INTERACTIVE)
   gtk_message_box (message);
 else
#endif
   fprintf (stderr, "print: %s\n", message);
}
