/*
 *
 *   Print plug-in Datamax-O'Neil DPL driver for Gutenprint.
 *
 *   Copyright 1997-2000 Michael Sweet (mike@easysw.com),
 *	Robert Krawitz (rlk@alum.mit.edu) and
 *      Dave Hill (dave@minnie.demon.co.uk)
 *
 *   Copyright 2016 Steve Letter (sletter1@yahoo.com)
 *
 *   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, see <https://www.gnu.org/licenses/>.
 */

/*
 * This file must include only standard C header files.  The core code must
 * compile on generic platforms that don't support glib, gimp, gtk, etc.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gutenprint/gutenprint.h>
#include <gutenprint/gutenprint-intl-internal.h>
#include "gutenprint-internal.h"
#include "dither-impl.h"
#include <stdio.h>
#include <string.h>

#define DEBUG
#define DPL_DEBUG_DISABLE_BLANKLINE_REMOVAL

/*
 * Local functions...
 */
static void dpl_pcx (stp_vars_t *, unsigned char *, int, int);
static int dpl_get_multiplier (const stp_vars_t * v);

#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif /* !MAX */

typedef struct
{
  int height;
  int orientation;
  int label_separator;
  unsigned int h_offset;
  unsigned int v_offset;
  int darkness;
  int speed;
  int present;
} dpl_privdata_t;

/*
 * Generic define for a name/value set
 */

typedef struct
{
  const char *dpl_name;
  const char *dpl_text;
  int dpl_code;
  int p0;
  int p1;
} dpl_t;


#define DPL_RES_150_150		1
#define DPL_RES_203_203		2
#define DPL_RES_300_300		4
#define DPL_RES_400_400		8
#define DPL_RES_600_600		16

static const dpl_t dpl_resolutions[] = {
  {"600dpi", N_("600x600 DPI"), DPL_RES_600_600, 600, 600},
  {"400dpi", N_("400x400 DPI"), DPL_RES_400_400, 400, 400},
  {"300dpi", N_("300x300 DPI"), DPL_RES_300_300, 300, 300},
  {"203dpi", N_("203x203 DPI"), DPL_RES_203_203, 203, 203},
  {"150dpi", N_("150x150 DPI"), DPL_RES_150_150, 150, 150},
};

#define NUM_RESOLUTIONS		(sizeof(dpl_resolutions) / sizeof (dpl_t))

static const dpl_t dpl_speeds[] = {
  {"A", N_("1.0 IPS"), 'A'},
  {"B", N_("1.5 IPS"), 'B'},
  {"C", N_("2.0 IPS"), 'C'},
  {"D", N_("2.5 IPS"), 'D'},
  {"E", N_("3.0 IPS"), 'E'},
  {"F", N_("3.5 IPS"), 'F'},
  {"G", N_("4.0 IPS"), 'G'},
  {"H", N_("4.5 IPS"), 'H'},
  {"I", N_("5.0 IPS"), 'I'},
  {"J", N_("5.5 IPS"), 'J'},
  {"K", N_("6.0 IPS"), 'K'},
  {"L", N_("6.5 IPS"), 'L'},
  {"M", N_("7.0 IPS"), 'M'},
  {"N", N_("7.5 IPS"), 'N'},
  {"O", N_("8.0 IPS"), 'O'},
  {"P", N_("8.5 IPS"), 'P'},
  {"Q", N_("9.0 IPS"), 'Q'},
  {"R", N_("9.5 IPS"), 'R'},
  {"S", N_("10.0 IPS"), 'S'},
  {"T", N_("10.5 IPS"), 'T'},
  {"U", N_("11.0 IPS"), 'U'},
  {"V", N_("11.5 IPS"), 'V'},
  {"W", N_("12.0 IPS"), 'W'},
};

#define NUM_SPEEDS		(sizeof(dpl_speeds) / sizeof (dpl_t))

/*
 * Printer capability data
 */

typedef struct
{
  int model;
  int custom_max_width;
  int custom_max_height;
  int custom_min_width;
  int custom_min_height;
  int resolutions;
  int max_resolution;
  int resolution_adjust;
  char max_speed;
  char min_speed;
  char default_speed;
} dpl_cap_t;

static const dpl_cap_t dpl_model_capabilities[] = {
/* Datamax-O'Neil Thermal DPL printers */
  {10017,			/* I Class Mark II 203 DPI */
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_203_203,
   DPL_RES_203_203,
   DPL_RES_203_203,
   'W',
   'C',
   'O',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10018,			/* I Class Mark II 300 DPI */
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_150_150 | DPL_RES_300_300,	/* Resolutions */
   DPL_RES_300_300,
   DPL_RES_203_203,
   'S',
   'C',
   'O',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10020,			/* I Class Mark II 600 DPI */
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   /* for future use
      DPL_RES_150_150 | DPL_RES_203_203 | DPL_RES_300_300 | DPL_RES_600_600, */
   DPL_RES_300_300 | DPL_RES_600_600,	/* Resolutions */
   DPL_RES_600_600,
   DPL_RES_300_300,
   'K',
   'C',
   'G',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10021,			/* E Class Mark III Basic 203 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_203_203,	/* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'G',
   'C',
   'E',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10022,			/* E Class Mark III Basic 300 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_300_300,	/* Resolutions */
   DPL_RES_300_300,
   DPL_RES_300_300,
   'G',
   'C',
   'E',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10023,			/* E Class Mark III Advanced 203 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_203_203,	/* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'I',
   'C',
   'E',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10024,			/* E Class Mark III Advanced 300 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_300_300,	/* Resolutions */
   DPL_RES_300_300,
   DPL_RES_300_300,
   'I',
   'C',
   'G',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10025,			/* E Class Mark III Pro 203 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_203_203,	/* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'K',
   'C',
   'G',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10026,			/* E Class Mark III Pro 300 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_300_300,	/* Resolutions */
   DPL_RES_300_300,
   DPL_RES_300_300,
   'I',
   'C',
   'G',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10027,			/* E Class Mark III ProPlus 203 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_203_203,	/* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'K',
   'C',
   'G',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10028,			/* E Class Mark III ProPlus 300 DPI*/
   4 * 72, 99 * 72,		/* Max paper size */
   1, 1,			/* Min paper size */
   DPL_RES_300_300,	/* Resolutions */
   DPL_RES_300_300,
   DPL_RES_300_300,
   'I',
   'C',
   'G',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10029,                       /* RL3e */
   3 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,     /* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'G',
   'A',
   'E',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10030,                       /* RL4e */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,     /* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'G',
   'A',
   'E',
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10031,                       /* H4212 */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,             /* Resolutions */
   DPL_RES_203_203,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'W',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10032,                       /* H4212X */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,             /* Resolutions */
   DPL_RES_203_203,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'W',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10033,                       /* H4310 */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_150_150 | DPL_RES_300_300,   /* Resolutions */
   DPL_RES_300_300,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'S',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10034,                       /* H4310X */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_150_150 | DPL_RES_300_300,   /* Resolutions */
   DPL_RES_300_300,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'S',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10035,                       /* H4408 */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_400_400 | DPL_RES_203_203,   /* Resolutions */
   DPL_RES_203_203,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'O',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'G',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10036,                       /* H4606 */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_300_300 | DPL_RES_600_600,   /* Resolutions */
   DPL_RES_600_600,             /* Max Resolution */
   DPL_RES_300_300,             /* Resolution Adjust */
   'K',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'G',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10037,                       /* H4606X */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_300_300 | DPL_RES_600_600,   /* Resolutions */
   DPL_RES_600_600,             /* Max Resolution */
   DPL_RES_300_300,             /* Resolution Adjust */
   'K',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'G',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10038,                       /* H6210 */
   6 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,             /* Resolutions */
   DPL_RES_203_203,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'S',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10039,                       /* H6210X */
   6 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,             /* Resolutions */
   DPL_RES_203_203,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'S',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10040,                       /* H6212 */
   6 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,             /* Resolutions */
   DPL_RES_203_203,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'W',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10041,                       /* H6212X */
   6 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,             /* Resolutions */
   DPL_RES_203_203,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'W',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10042,                       /* H6308 */
   6 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_150_150 | DPL_RES_300_300,   /* Resolutions */
   DPL_RES_300_300,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'O',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'G',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10043,                       /* H6310X */
   6 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_150_150 | DPL_RES_300_300,   /* Resolutions */
   DPL_RES_300_300,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'S',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'O',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10044,                       /* H8308 */
   8.5 * 72, 99 * 72,           /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_150_150 | DPL_RES_300_300,   /* Resolutions */
   DPL_RES_300_300,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'O',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'G',                         /* Default IPS */
   },
/* Datamax-O'Neil Thermal DPL printers */
  {10045,                       /* H8308X */
   8.5 * 72, 99 * 72,           /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_150_150 | DPL_RES_300_300,   /* Resolutions */
   DPL_RES_300_300,             /* Max Resolution */
   DPL_RES_203_203,             /* Resolution Adjust */
   'O',                         /* Maximum IPS */
   'C',                         /* Minimum IPS */
   'G',                         /* Default IPS */
   },
/* Honeywell Thermal DPL printers */
  {10046,                       /* RP2 */
   2 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,     /* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'I',
   'A',
   'E',
   },
/* Honeywell Thermal DPL printers */
  {10047,                       /* RP4 */
   4 * 72, 99 * 72,             /* Max paper size */
   1, 1,                        /* Min paper size */
   DPL_RES_203_203,     /* Resolutions */
   DPL_RES_203_203,
   DPL_RES_203_203,
   'I',
   'A',
   'E',
   },
};

static const stp_parameter_t the_parameters[] = {
  {
   "PageSize", N_("Page Size"), "Color=No,Category=Basic Printer Setup",
   N_("Size of the paper being printed to"),
   STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
   STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0},
  {
   "Resolution", N_("Resolution"), "Color=No,Category=Basic Printer Setup",
   N_("Resolution of the print"),
   STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
   STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0},
  {
    "PrintingMode", N_("Printing Mode"), "Color=Yes,Category=Core Parameter",
    N_("Printing Output Mode"),
    STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_CORE,
    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
  },
#ifdef FIXME  /* Orientation not available for graphics, need
               * rotation routine in this driver */
  {
   "Orientation", N_("Orientation"), "Color=No,Category=Basic Printer Setup",
   N_("Orientation, Portrait, Landscape, Upside Down, Seascape"),
   STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
   STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0
   },
#endif
  {
   "LabelSeparator", N_("Media Index Type"),
   "Color=No,Category=Basic Printer Setup",
   N_("Gap, Notch, Hole, Black Mark, Continuous"),
   STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
   STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0,
   },
  {
   "Darkness", N_("Darkness"), "Color=No,Category=Basic Printer Setup",
   N_("Darkness Adjust, from 0 to 30"),
   STP_PARAMETER_TYPE_INT, STP_PARAMETER_CLASS_FEATURE,
   STP_PARAMETER_LEVEL_BASIC, 0, 1, STP_CHANNEL_NONE, 1, 0},
  {
   "Speed", N_("Print Speed"), "Color=No,Category=Basic Printer Setup",
   N_("Speed Adjust"),
   STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_FEATURE,
   STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 0, 0},
};

static const int the_parameter_count =
  sizeof (the_parameters) / sizeof (const stp_parameter_t);

typedef struct
{
  const stp_parameter_t param;
  double min;
  double max;
  double defval;
  int color_only;
} float_param_t;

static const float_param_t float_parameters[] = {
  {
   {
    "HorizOffset", N_("Horizontal Offset"),
    "Color=No,Category=Basic Output Adjustment",
    N_("Adjust horizontal position"),
    STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
    STP_PARAMETER_LEVEL_ADVANCED3, 1, 1, STP_CHANNEL_NONE, 1, 0,
    }, 0.0, 4.0, 0.0, 0},
  {
   {
    "VertOffset", N_("Vertical Offset"),
    "Color=No,Category=Basic Output Adjustment",
    N_("Adjust vertical position"),
    STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
    STP_PARAMETER_LEVEL_ADVANCED3, 1, 1, STP_CHANNEL_NONE, 1, 0,
    }, 0.0, 10.0, 0.0, 0},
  {
   {
    "Present", N_("Present Distance"),
    "Color=No,Category=Basic Output Adjustment",
    N_("Presnt Distance, 0.0 advances the default."),
    STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_FEATURE,
    STP_PARAMETER_LEVEL_BASIC, 1, 1, STP_CHANNEL_NONE, 1, 0,
    }, 0.0, 10.0, 0.0, 0},
};

static const int float_parameter_count =
  sizeof (float_parameters) / sizeof (const float_param_t);

/*
 * Convert a value into it's option name
 */

static const char *
dpl_val_to_string (int code,	/* I: Code */
		   const dpl_t * options,	/* I: Options */
		   int num_options)	/* I: Num options */
{

  int i;
  const char *string = NULL;

  /*
   * Look up the code in the table and convert to the string.
   */

  for (i = 0; i < num_options; i++)
    {
      if (code == options[i].dpl_code)
	{
	  string = options[i].dpl_name;
	  break;
	}
    }

  stp_deprintf (STP_DBG_DPL, "Code: %d, String: %s\n", code, string);

  return (string);
}

static const char *
dpl_val_to_text (int code,	/* I: Code */
		 const dpl_t * options,	/* I: Options */
		 int num_options)	/* I: Num options */
{

  int i;
  const char *string = NULL;

  /*
   * Look up the code in the table and convert to the string.
   */

  for (i = 0; i < num_options; i++)
    {
      if (code == options[i].dpl_code)
	{
	  string = gettext (options[i].dpl_text);
	  break;
	}
    }

  stp_deprintf (STP_DBG_DPL, "Code: %d, String: %s\n", code, string);

  return (string);
}

/*
 * dpl_get_model_capabilities() - Return struct of model capabilities
 */

static const dpl_cap_t *	/* O: Capabilities */
dpl_get_model_capabilities (int model)	/* I: Model */
{
  int i;
  int models = sizeof (dpl_model_capabilities) / sizeof (dpl_cap_t);
  for (i = 0; i < models; i++)
    {
      if (dpl_model_capabilities[i].model == model)
	{
	  return &(dpl_model_capabilities[i]);
	}
    }
  stp_erprintf ("dpl: model %d not found in capabilities list.\n", model);
  return &(dpl_model_capabilities[0]);
}

/*
 * Determine the current resolution
 */

static void
dpl_describe_resolution (const stp_vars_t * v, int *x, int *y)
{
  int i;
  const char *resolution = stp_get_string_parameter (v, "Resolution");

  *x = -1;
  *y = -1;
  if (resolution)
    {
      for (i = 0; i < NUM_RESOLUTIONS; i++)
	{
	  if (!strcmp (resolution, dpl_resolutions[i].dpl_name))
	    {
	      *x = dpl_resolutions[i].p0;
	      *y = dpl_resolutions[i].p1;
	    }
	}
    }
  if (*x != *y) {
    if (*x > *y) {
      *y = *x;
    } else {
      *x = *y;
    }
  }
}

int
dpl_get_multiplier (const stp_vars_t * v)
{
  int x, y, multiplier;
  int i;
  int max_dpi;
  int model = stp_get_model_id (v);
  const dpl_cap_t *caps = dpl_get_model_capabilities (model);

  for (i = 0; i < NUM_RESOLUTIONS; i++)
    {
      if (caps->max_resolution == dpl_resolutions[i].dpl_code)
	{
	  max_dpi = dpl_resolutions[i].p0;
	}
    }

  dpl_describe_resolution (v, &x, &y);

  if (x == max_dpi)
    multiplier = 1;
  else
    multiplier = 2;

  return multiplier;
}

#ifdef FIXME
/*
 * Orientation support - modes available
 * Note that the internal names MUST match those in cups/genppd.c else the
 * PPD files will not be generated correctly
 */

static const stp_param_string_t orientation_types[] = {
  {"Portrait", N_("Portrait")},
  {"Landscape", N_("Landscape")},
  {"UpsideDown", N_("Reverse Portrait")},
  {"Seascape", N_("Reverse Landscape")},
};

#define NUM_ORIENTATION (sizeof (orientation_types) / sizeof (stp_param_string_t))
#endif

/*
 * Label Separator Support for D-O printers, modes available
 */

static const stp_param_string_t label_separator_types[] = {
  {"IGNORE", N_("Printer Setting")},
  {"GAP", N_("Gap")},
  {"NOTCH", N_("Notch")},
  {"HOLE", N_("Hole")},
  {"MARK", N_("Black Mark")},
  {"CONTINUOUS", N_("Continuous")},
};

#define NUM_LABEL_SEPARATOR (sizeof (label_separator_types) / sizeof (stp_param_string_t))

/*
 * 'dpl_papersize_valid()' - Is the paper size valid for this printer.
 */

static int
dpl_papersize_valid (const stp_papersize_t * pt, int model)
{
  const dpl_cap_t *caps = dpl_get_model_capabilities (model);
  unsigned int pwidth = pt->width;
  unsigned int pheight = pt->height;

/*
 * Is it a valid name?
 */

  if (strlen (pt->name) <= 0)
    return (0);

/*
 * We are allowed custom paper sizes. Check that the size is within
 * limits.  Check that the name contains d-o if this is the
 * Datamax O'Neil label printer and not custom paper
 */

  if (pwidth <= caps->custom_max_width &&
      pheight <= caps->custom_max_height &&
      (pheight >= caps->custom_min_height || pheight == 0) &&
      (pwidth >= caps->custom_min_width || pwidth == 0))
    {
      if (strcmp (pt->name, "Custom"))
	{
	  if (NULL != strstr (pt->name, "d-o"))
	    {
	      return (1);
	    }
	  else
	    {
	      return (0);
	    }
	}
    }

  return (0);
}

/*
 * 'dpl_parameters()' - Return the parameter values for the given parameter.
 */

static stp_parameter_list_t
dpl_list_parameters (const stp_vars_t * v)
{
  stp_parameter_list_t *ret;
  int i;

  ret = stp_parameter_list_create ();
  for (i = 0; i < the_parameter_count; i++)
    stp_parameter_list_add_param (ret, &(the_parameters[i]));
  for (i = 0; i < float_parameter_count; i++)
    stp_parameter_list_add_param (ret, &(float_parameters[i].param));
  return ret;
}

static void
dpl_parameters (const stp_vars_t * v, const char *name,
		stp_parameter_t * description)
{
  int model = stp_get_model_id (v);
  int i;
  const dpl_cap_t *caps;
  description->p_type = STP_PARAMETER_TYPE_INVALID;

  if (name == NULL)
    return;

  stp_deprintf (STP_DBG_DPL, "dpl_parameters(): Name = %s\n", name);

  caps = dpl_get_model_capabilities (model);

  stp_deprintf (STP_DBG_DPL, "Printer model = %d\n", model);
  stp_deprintf (STP_DBG_DPL, "PageWidth = %d, PageHeight = %d\n",
		caps->custom_max_width, caps->custom_max_height);
  stp_deprintf (STP_DBG_DPL, "MinPageWidth = %d, MinPageHeight = %d\n",
		caps->custom_min_width, caps->custom_min_height);
  stp_deprintf (STP_DBG_DPL, "Resolutions: %d\n", caps->resolutions);

  for (i = 0; i < the_parameter_count; i++)
    if (strcmp (name, the_parameters[i].name) == 0)
      {
	stp_fill_parameter_settings (description, &(the_parameters[i]));
	break;
      }
  description->deflt.str = NULL;

  for (i = 0; i < float_parameter_count; i++)
    if (strcmp (name, float_parameters[i].param.name) == 0)
      {
	stp_fill_parameter_settings (description,
				     &(float_parameters[i].param));
	description->deflt.dbl = float_parameters[i].defval;
	description->bounds.dbl.upper = float_parameters[i].max;
	description->bounds.dbl.lower = float_parameters[i].min;
	break;
      }

  if (strcmp (name, "PageSize") == 0)
    {
      int papersizes = stp_known_papersizes ();
      description->bounds.str = stp_string_list_create ();
      for (i = 0; i < papersizes; i++)
	{
	  const stp_papersize_t *pt = stp_get_papersize_by_index (i);
	  if (strlen (pt->name) > 0 && dpl_papersize_valid (pt, model))
	    stp_string_list_add_string (description->bounds.str,
					pt->name, gettext (pt->text));
	}
      description->deflt.str =
	stp_string_list_param (description->bounds.str, 0)->name;
    }
  else if (strcmp (name, "Resolution") == 0)
    {
      description->bounds.str = stp_string_list_create ();
      description->deflt.str =
	dpl_val_to_string (caps->max_resolution, dpl_resolutions,
			   NUM_RESOLUTIONS);
      for (i = 0; i < NUM_RESOLUTIONS; i++)
	if (caps->resolutions & dpl_resolutions[i].dpl_code)
	  {
	    stp_string_list_add_string
	      (description->bounds.str,
	       dpl_val_to_string (dpl_resolutions[i].dpl_code,
				  dpl_resolutions, NUM_RESOLUTIONS),
	       dpl_val_to_text (dpl_resolutions[i].dpl_code,
				dpl_resolutions, NUM_RESOLUTIONS));
	  }
    }
  else if (strcmp(name, "PrintingMode") == 0)
    {
      description->bounds.str = stp_string_list_create();
      stp_string_list_add_string
        (description->bounds.str, "BW", _("Black and White"));
      description->deflt.str =
        stp_string_list_param(description->bounds.str, 0)->name;
    }
#ifdef FIXME
  else if (strcmp (name, "Orientation") == 0)
    {
      description->bounds.str = stp_string_list_create ();
      description->deflt.str = orientation_types[0].name;
      for (i = 0; i < NUM_ORIENTATION; i++)
	{
	  stp_string_list_add_string (description->bounds.str,
				      orientation_types[i].name,
				      gettext (orientation_types[i].text));
	}
    }
#endif
  else if (strcmp (name, "LabelSeparator") == 0)
    {
      description->bounds.str = stp_string_list_create ();
      description->deflt.str = label_separator_types[0].name;
      for (i = 0; i < NUM_LABEL_SEPARATOR; i++)
	{
	  stp_string_list_add_string (description->bounds.str,
				      label_separator_types[i].name,
				      gettext (label_separator_types[i].
					       text));
	}
    }
  else if (strcmp (name, "Darkness") == 0)
    {
      description->deflt.integer = -1;
      description->bounds.integer.lower = 0;
      description->bounds.integer.upper = 30;
    }
  else if (strcmp (name, "Speed") == 0)
    {
      description->bounds.str = stp_string_list_create ();
      stp_string_list_add_string (description->bounds.str, "None",
                                  _("Use Current Setting"));
      stp_string_list_add_string (description->bounds.str, "Default",
                                  _("Use Default Setting"));
      description->deflt.str = "None";
      for (i = 0; i < NUM_SPEEDS; i++)
	{
	  stp_string_list_add_string (description->bounds.str,
				      dpl_speeds[i].dpl_name,
				      gettext (dpl_speeds[i].
					       dpl_text));
	}
    }
  else if (strcmp (name, "HorizOffset") == 0 ||
	   strcmp (name, "VertOffset") == 0 || strcmp (name, "Present") == 0)
    {
      description->is_active = 1;
    }
}


/*
 * 'dpl_imageable_area()' - Return the imageable area of the page.
 */
static void
internal_imageable_area (const stp_vars_t * v,	/* I */
			 int *left,	/* O - Left position in points */
			 int *right,	/* O - Right position in points */
			 int *bottom,	/* O - Bottom position in points */
			 int *top)	/* O - Top position in points */
{
  int width, height;		/* Size of page */

  stp_default_media_size (v, &width, &height);

  *left = 0;
  *right = width;
  *top = 0;
  *bottom = height;
}

static void
dpl_imageable_area (const stp_vars_t * v,	/* I */
		    int *left,	/* O - Left position in points */
		    int *right,	/* O - Right position in points */
		    int *bottom,	/* O - Bottom position in points */
		    int *top)	/* O - Top position in points */
{
  internal_imageable_area (v, left, right, bottom, top);
}

static void
dpl_limit (const stp_vars_t * v,	/* I */
	   int *width, int *height, int *min_width, int *min_height)
{
  const dpl_cap_t *caps = dpl_get_model_capabilities (stp_get_model_id (v));
  *width = caps->custom_max_width;
  *height = caps->custom_max_height;
  *min_width = caps->custom_min_width;
  *min_height = caps->custom_min_height;
}

static const char *
dpl_describe_output (const stp_vars_t * v)
{
  return "Grayscale";
}

static void
pcx_header (stp_vars_t * v, stp_image_t * image)
{
  unsigned short height;
  unsigned short right;
  unsigned short top;		/* y = 0 is at bottom */
  unsigned short bytes;
  short xdpi;
  int i_xdpi;
  int *xdpi_p = (&i_xdpi);
  short ydpi;
  int i_ydpi;
  int *ydpi_p = (&i_ydpi);
  int n;
  const short zero = 0;

  stp_putc (10, v);		/* Signature */
  stp_putc (5, v);		/* Version */
  stp_putc (1, v);		/* RLE encoding */
  stp_putc (1, v);		/* bits per pixel */

  /* Get resolutions */
  dpl_describe_resolution (v, xdpi_p, ydpi_p);

  xdpi = (short) i_xdpi;
  ydpi = (short) i_ydpi;

  bytes = (xdpi * 4 + 7 ) / 8;	/* must be an even number */
  if (bytes != (bytes & 0xfffe))
    bytes++;

  height = stp_image_height (image);

  /*
   * Convert image size to printer resolution and setup the page for printing...
   */

  right = 4 * xdpi - 1;
  top = height - 1;

  /* send image start and end positions */
  stp_zfwrite ((const char *) &zero, 2, 1, v);
  stp_zfwrite ((const char *) &zero, 2, 1, v);
  stp_zfwrite ((const char *) &right, 2, 1, v);
  stp_zfwrite ((const char *) &top, 2, 1, v);

  /* send resolutions */
  stp_zfwrite ((const char *) &xdpi, 2, 1, v);
  stp_zfwrite ((const char *) &ydpi, 2, 1, v);

  /* send palette and reserved byte */
  for (n = 0; n < 3; n++)
    stp_putc (0, v);
  for (n = 0; n < 45; n++)
    stp_putc (0xff, v);
  stp_putc (0, v);

  stp_putc (1, v);		/* number of planes, monochrome */

  stp_zfwrite ((const char *) &bytes, 2, 1, v);

  stp_putc (1, v);		/* monochrome */
  stp_putc (0, v);

  stp_putc (0, v);		/* imagee size */
  stp_putc (0, v);
  stp_putc (0, v);
  stp_putc (0, v);

  for (n = 0; n < 54; n++)
    stp_putc (0, v);		/* padding */
}

/*
 * 'dpl_print()' - Print an image to an HP printer.
 */

static void
dpl_printfunc (stp_vars_t * v, int height)
{
  unsigned char *black = stp_dither_get_channel (v, STP_ECOLOR_K, 0);

  dpl_pcx (v, black, (height + 7) / 8, 1);
}

static double
get_double_param (stp_vars_t * v, const char *param)
{
  if (param && stp_check_float_parameter (v, param, STP_PARAMETER_ACTIVE))
    return stp_get_float_parameter (v, param);
  else
    return 1.0;
}

static int
dpl_do_print (stp_vars_t * v, stp_image_t * image)
{
  dpl_privdata_t privdata;
  int status = 1;
#ifdef FIXME
  const char *orientation_mode = stp_get_string_parameter (v, "Orientation");
#endif
  const char *label_separator_mode =
    stp_get_string_parameter (v, "LabelSeparator");
  double h_offset = get_double_param (v, "HorizOffset");
  double v_offset = get_double_param (v, "VertOffset");
  double present = get_double_param (v, "Present");
  int y;			/* Looping vars */
  int xdpi, ydpi;		/* Resolution */
  int multiplier;
  unsigned char *black;		/* Black bitmap data */
  unsigned zero_mask;
  int image_height;
  int image_width;
  int model = stp_get_model_id (v);
  const dpl_cap_t *caps = dpl_get_model_capabilities (model);
  const char *speed = stp_get_string_parameter(v, "Speed");

  if (!stp_verify (v))
    {
      stp_eprintf (v, "Print options not verified; cannot print.\n");
      return 0;
    }

  /*
   * Setup a read-only pixel region for the entire image...
   */

  stp_image_init (image);

  stp_set_string_parameter (v, "ColorCorrection", "None");
#ifdef TESTING
  stp_set_float_parameter (v, "Brightness", 1.0);
  stp_set_float_parameter (v, "Contrast", 1.0);
  stp_set_float_parameter (v, "Gamma", 1.0);
#endif
  /*
   * Figure out the output resolution...
   */

  dpl_describe_resolution (v, &xdpi, &ydpi);

  stp_deprintf (STP_DBG_DPL, "dpl: resolution=%dx%d\n", xdpi, ydpi);
  if (xdpi <= 0 || ydpi <= 0)
    {
      stp_eprintf (v, "No resolution found; cannot print.\n");
      return 0;
    }

  image_height = stp_image_height (image);
  image_width = stp_image_width (image);

#ifdef FIXME
  privdata.orientation = 0;
  if ((strncmp (orientation_mode, "Landscape", 9) == 0))
    privdata.orientation = 1;
  else if ((strncmp (orientation_mode, "UpsideDown", 10) == 0))
    privdata.orientation = 2;
  else if ((strncmp (orientation_mode, "Seascape", 8) == 0))
    privdata.orientation = 3;
#endif

  /*
   * Label Separator mode
   */
  privdata.label_separator = 0;
  if ((strncmp (label_separator_mode, "GAP", 3) == 0))
    privdata.label_separator = 1;
  else if ((strncmp (label_separator_mode, "NOTCH", 5) == 0))
    privdata.label_separator = 1;
  else if ((strncmp (label_separator_mode, "HOLE", 4) == 0))
    privdata.label_separator = 1;
  else if ((strncmp (label_separator_mode, "MARK", 4) == 0))
    privdata.label_separator = 2;
  else if ((strncmp (label_separator_mode, "CONTINUOUS", 10) == 0))
    privdata.label_separator = 3;

  /*
   * Print Offsets
   */
  privdata.h_offset = (int) (h_offset * 100);	/* in 0.01 of an inch */
  privdata.v_offset = (int) (v_offset * 100);   /* in 0.01 of an inch */

  privdata.present = (int) (present * 100.0);   /* in 0.01 of an inch */

  /*
   * Darkness Mode
   */
  if (-1 != (privdata.darkness = stp_get_int_parameter (v, "Darkness")))
    {
      if (0 == privdata.darkness)
	{
	  privdata.darkness = 10;	/* default */
	}
    }

  /*
   * Speed Mode
   */
  privdata.speed = 0;
  if (0 != strcmp("None", speed))
    {
      if (0 == strcmp("Default", speed))
	{
          privdata.speed = (int) caps->default_speed;
        }
      else
        {
          int i;
          for (i = 0; i < NUM_SPEEDS; i++)
            {
              if (0 == strcmp(dpl_speeds[i].dpl_name, speed))
                {
                  privdata.speed = dpl_speeds[i].dpl_code;
                  break;
                }
            }
	  if (caps->min_speed > (char) (privdata.speed))
	    {
	      privdata.speed = caps->min_speed;
	    }
	  else
	    {
	      if (caps->max_speed < (char) (privdata.speed))
		{
		  privdata.speed = caps->max_speed;
		}
	    }
	}
    }

  /* workaround for printer bug */
  for (y=0; y<64; y++)
    stp_putc (0, v);

  /*
   * Send DPL initialization commands...
   */
  stp_puts ("\002n\r", v);	/* set Imperial units */

  /* Max page length */
  if (image_height / ydpi > 4)
    {
      stp_zprintf (v, "\002M%04i\r",
		   300 * image_height / ydpi + (3 * privdata.v_offset));
    }
  else
    {
      stp_zprintf (v, "\002M%04i\r", 1200 + (3 * privdata.v_offset));
    }
  /* set Label Width */
  stp_zprintf (v, "\002KcLW%04i\r",
	       100 * image_width / xdpi + privdata.h_offset);
  if (0 != privdata.label_separator)
    {
      if (1 == privdata.label_separator)
	{
	  stp_puts ("\002e\r", v);	/* edge mode */
	}
      else if (2 == privdata.label_separator)
	{
	  stp_puts ("\002r\r", v);	/* Mark mode */
	}
      else
	{
	  stp_zprintf (v, "\002c%04i\r", 100 *	/* Continuous mode */
		       image_height / ydpi + privdata.v_offset);
	}
    }
  if (privdata.darkness > -1)
    {
      stp_zprintf (v, "\002KZH%02i\r", privdata.darkness);
    }
  if (privdata.speed > 0)
    {
      stp_zprintf (v, "\002KZP%c\r", privdata.speed);
    }
  stp_zprintf (v, "\002Kf%04i\r", privdata.present);
  stp_puts ("\002IDPcups0\r", v);	/* Save PCX file */
  pcx_header (v, image);


  stp_deprintf (STP_DBG_DPL, "Normal init\n");

  /*
   * Allocate memory for the raster data...
   */

  black = stp_malloc ((image_width + 7) / 8);

  stp_set_string_parameter (v, "STPIOutputType", "Grayscale");

  /* set up for very fast dithering as default */
  stp_set_string_parameter (v, "DitherAlgorithm", "VeryFast");

  stp_dither_init (v, image, image_width, xdpi, ydpi);

  stp_dither_add_channel (v, black, STP_ECOLOR_K, 0);
  stp_channel_set_black_channel (v, STP_ECOLOR_K);

  stp_channel_set_density_adjustment (v, STP_ECOLOR_K, 0,
				      get_double_param (v, "BlackDensity") *
				      1);

  (void) stp_color_init (v, image, 65536);

  stp_allocate_component_data (v, "Driver", NULL, NULL, &privdata);

  for (y = 0; y < image_height; y++)
    {
      if (stp_color_get_row (v, image, y, &zero_mask))
	{
	  status = 2;
	  break;
	}
      stp_dither (v, y, 0, 0, NULL);
      dpl_printfunc (v, image_width);
    }

  stp_puts ("\r\002L\r", v);	/* enter Label Formatting mode */
  multiplier = dpl_get_multiplier (v);	/* dot multiplier */
  stp_zprintf (v, "D%1i%1i\r", multiplier, multiplier);
  stp_puts ("R0000\r", v);	/* 0 offset, offset handled below */
  stp_puts ("A2\r", v);		/* transparent mode */
  /* load graphic */
  stp_zprintf (v, "1Y11000%04i%04icups0\r", privdata.v_offset,
	       privdata.h_offset);
  stp_puts ("Q0001\r", v);	/* one label */
  stp_puts ("E\r", v);		/* print now */
  stp_puts ("\002xDGcups0\r", v);	/* delete graphic */
#if 0
  stp_puts ("\002zD\r", v);	/* reclaim space */
#endif

  stp_image_conclude (image);

  /*
   * Cleanup...
   */

  if (black != NULL)
    stp_free (black);

  return status;
}

static int
dpl_print (const stp_vars_t * v, stp_image_t * image)
{
  int status;
  stp_vars_t *nv = stp_vars_create_copy (v);
  stp_prune_inactive_options (nv);
  status = dpl_do_print (nv, image);
  stp_vars_destroy (nv);
  return status;
}

static const stp_printfuncs_t print_dpl_printfuncs = {
  dpl_list_parameters,
  dpl_parameters,
  stp_default_media_size,
  dpl_imageable_area,
  dpl_imageable_area,
  dpl_limit,
  dpl_print,
  dpl_describe_resolution,
  dpl_describe_output,
  stp_verify_printer_params,
  NULL,
  NULL,
  NULL
};


static void
dpl_pcx (stp_vars_t * v,	/* I - Print file or command */
	 unsigned char *short_line,	/* I - Output bitmap data */
	 int height,		/* I - Height of bitmap data */
	 int last_plane)	/* I - True if this is the last plane */
{
  unsigned char *line;
  unsigned char *data;
  unsigned char stored;
  int add_bytes = 0;
  int count = 0;
  int in = 0;
  int out = 0;
  int xdpi, ydpi;
  int model = stp_get_model_id (v);
  const dpl_cap_t *caps = dpl_get_model_capabilities (model);
  int i;
  int max_dpi;
  int dpi_adjust;

  /* Each line has to be 4 inches long */
  dpl_describe_resolution (v, &xdpi, &ydpi);

  for (i = 0; i < NUM_RESOLUTIONS; i++)
    {
      if (caps->max_resolution == dpl_resolutions[i].dpl_code)
	{
	  max_dpi = dpl_resolutions[i].p0;
	}
      if (caps->resolution_adjust == dpl_resolutions[i].dpl_code)
	{
	  dpi_adjust = dpl_resolutions[i].p0;
	}
    }

  if (xdpi == max_dpi)
    {
      add_bytes = ((xdpi * 4) + 7) / 8 - height;
    }
  else
    {
      add_bytes = ((dpi_adjust * 4) + 7) / 8 - height;
    }

  /* allocate 4 inch input buffer */
  line = (unsigned char *) stp_malloc (height + add_bytes);
  /* allocate output buffer, worst case */
  data = (unsigned char *) stp_malloc ((height + add_bytes) * 2);

  /* invert data, cups makes white 1 and black 0, printer wants the opposite */
  for (in = 0; in < height; in++)
    {
      line[in] = 0xff ^ short_line[in];
    }

  /* pad to 4 inches */
  for (in = height; in < (height + add_bytes); in++)
    {
      line[in] = 0xff;
    }

  in = 0;
  while (in < (height + add_bytes))
    {
      stored = line[in];	/* save the value */
      for (count = 1; in + count < (height + add_bytes) && line[in + count] == stored && count < 63; count++);	/* count the run */

      /* test to see if we need to make a run of one because the data value
         has the two top bits set and see if we actually have a run */
      if (stored > 191 || count > 1)
	{
	  data[out++] = count | 0xc0;	/* mask to indicate a run */
	  data[out++] = stored;	/* output the value */
	}
      else			/* not a run */
	{
	  data[out++] = stored;	/* output the value */
	}
      in += count;
    }
  stp_zfwrite ((const char *) data, out, 1, v);
  stp_free (line);
  stp_free (data);
}


static stp_family_t print_dpl_module_data = {
  &print_dpl_printfuncs,
  NULL
};


static int
print_dpl_module_init (void)
{
  return stp_family_register (print_dpl_module_data.printer_list);
}


static int
print_dpl_module_exit (void)
{
  return stp_family_unregister (print_dpl_module_data.printer_list);
}


/* Module header */
#define stp_module_version print_dpl_LTX_stp_module_version
#define stp_module_data print_dpl_LTX_stp_module_data

stp_module_version_t stp_module_version = { 0, 0 };

stp_module_t stp_module_data = {
  "dpl",
  VERSION,
  "DPL family driver",
  STP_MODULE_CLASS_FAMILY,
  NULL,
  print_dpl_module_init,
  print_dpl_module_exit,
  (void *) &print_dpl_module_data
};
