/*
 * "$Id: cupsprofile.c 343 2007-07-13 19:52:48Z mike $"
 *
 *   Do simple calibration pages to generate a color profile for CUPS.
 *
 *   Copyright 2007 by Apple Inc.
 *   Copyright 1993-2005 by Easy Software Products.
 *
 *   These coded instructions, statements, and computer programs are the
 *   property of Apple Inc. and are protected by Federal copyright
 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
 *   which should have been included with this file.  If this file is
 *   file is missing or damaged, see the license at "http://www.cups.org/".
 *
 * Contents:
 *
 *   main()            - Do a 4-pass profiling of a printer.
 *   get_calibration() - Get a calibration value from the user.
 *   safe_gets()       - Get a string from the user.
 *   send_prolog()     - Send the standard PostScript prolog.
 *   send_pass1()      - Send the first pass to set the densities.
 *   send_pass2()      - Send pass 2 to set the gamma correction.
 *   send_pass3()      - Show pass 3 to set the color adjustments.
 *   send_pass4()      - Send the 4th and final pass with the test image.
 *   send_trailer()    - Send the standard PostScript trailer.
 */

/*
 * Include necessary headers...
 */

#include "driver.h"
#include "string.h"
#include <errno.h>


/*
 * abs macro...
 */

#ifndef abs
#  define	abs(a)		((a) < 0 ? -(a) : (a))
#endif /* !abs */


/*
 * Local functions...
 */

float	get_calibration(const char *prompt, float minval, float maxval);
char	*safe_gets(const char *prompt, char *s, int size);
FILE	*send_prolog(const char *command);
void	send_pass1(FILE *fp);
void	send_pass2(FILE *fp, float kd, float rd, float yellow);
void	send_pass3(FILE *fp, float kd, float rd, float g, float yellow);
void	send_pass4(FILE *fp, float red, float green, float blue,
	           const char *profile, const char *simpleProfile);
int	send_trailer(FILE *fp);


/*
 * Local globals...
 */

static const char * const calhex = "FEDCBA9876543210";
					/* Calibration letters */


/*
 * 'main()' - Do a 4-pass profiling of a printer.
 */

int					/* O - Exit status */
main(int  argc,				/* I - Number of command-line args */
     char *argv[])			/* I - Command-line arguments */
{
  char	printer[255];			/* Printer name */
  char	pagesize[255];			/* Page size */
  char	resolution[255];		/* Resolution */
  char	mediatype[255];			/* Media type */
  char	simpleProfile[1024];		/* SimpleProfile string */
  char	command[1024];			/* Print command */
  char	*profile;			/* Profile in command */
  int	proflen;			/* Remaining space for profile */
  char	junk[255];			/* Temporary string */
  FILE	*fp;				/* Print file */
  float	kd, rd, g;			/* Black density, red density, gamma */
  float	color;				/* Color adjustment value */
  float	red, green, blue;		/* Red, green, and blue adjustments */
  float	cyan, magenta, yellow;		/* Cyan, magenta, and yellow values */
  float	m[3][3];			/* Transform matrix */


 /*
  * Show a welcome banner...
  */

  puts("CUPS Printer Profiling Tool " DDK_VERSION);
  puts("Copyright 1993-2005 by Easy Software Products, All Rights Reserved.");
  puts("");
  puts("This profiling tool works only with CUPS raster drivers.  If you have a");
  puts("PostScript printer or are using the HP-IJS or Foomatic drivers, press");
  puts("CTRL+C now to abort!");
  puts("");

 /*
  * Ask the user for the printer, page size, resolution, and media type.
  */

  safe_gets("Printer name [default]?", printer, sizeof(printer));
  safe_gets("Page size [default]?", pagesize, sizeof(pagesize));
  safe_gets("Resolution [default]?", resolution, sizeof(resolution));
  safe_gets("Media type [default]?", mediatype, sizeof(mediatype));

 /* 
  * Assemble the print command for each pass...
  */

  strcpy(command, "lp -s");
  if (printer[0])
  {
    strlcat(command, " -d ", sizeof(command));
    strlcat(command, printer, sizeof(command));
  }
  if (resolution[0])
  {
    strlcat(command, " -o resolution=", sizeof(command));
    strlcat(command, resolution, sizeof(command));
  }
  if (pagesize[0] || mediatype[0])
  {
    strlcat(command, " -o media=", sizeof(command));

    if (pagesize[0])
    {
      strlcat(command, pagesize, sizeof(command));
      if (mediatype[0])
        strlcat(command, ",", sizeof(command));
    }

    strlcat(command, mediatype, sizeof(command));
  }

  strlcat(command, " -o profile=", sizeof(command));

  profile = command + strlen(command);
  proflen = sizeof(command) - (profile - command);

 /*
  * Print pass 1 to set the densities.
  */

  strlcpy(profile, "1000,1000,1000,0,0,0,1000,0,0,0,1000", proflen);

  safe_gets("Press ENTER to print pass #1 or N to skip...", junk, sizeof(junk));

  if (!junk[0])
  {
    puts("Sending calibration pass #1 for density/saturation levels...");

    fp = send_prolog(command);
    send_pass1(fp);
    send_trailer(fp);

    puts("Calibration pass #1 sent.");
  }

 /*
  * Get the numbers for pass 1...
  */

  puts("");
  puts("Please select the character that corresponds to the black block that");
  puts("is 100% saturated (dark) while not bleeding through the paper.  If");
  puts("the saturation point appears to occur between two characters, enter");
  puts("both characters.");
  puts("");

  kd = get_calibration("Black density?", 0.25f, 1.0f);

  puts("");
  puts("Now select the character that corresponds to the yellow block that is");
  puts("100% saturated (dark) while not bleeding through the paper. If the");
  puts("saturation point appears to occur between two characters, enter both");
  puts("characters.");
  puts("");

  cyan    = kd;
  magenta = kd;
  yellow  = get_calibration("Yellow density?", 0.25f, 1.0f);

  puts("");
  puts("Now select the character that corresponds to the red block that is");
  puts("100% saturated (dark) while not bleeding through the paper. If the");
  puts("saturation point appears to occur between two characters, enter both");
  puts("characters.");
  puts("");

  rd = get_calibration("Red density?", 0.5f, 2.0f);

 /*
  * Print pass 2 to get the gamma correction to use...
  */

  puts("");
  puts("Thank you.  Now insert the page back into the printer and press the");
  puts("ENTER key to print calibration pass #2.");
  puts("");

  safe_gets("Press ENTER to print pass #2 or N to skip...", junk, sizeof(junk));

  if (!junk[0])
  {
    puts("Sending calibration pass #2 for gamma levels...");

    fp = send_prolog(command);
    send_pass2(fp, kd, rd, yellow);
    send_trailer(fp);

    puts("Calibration pass #2 sent.");
  }

 /*
  * Get the numbers for pass 2...
  */

  puts("");
  puts("Please select the character that corresponds to the column of gray");
  puts("blocks that appear to be 1/2 and 1/4 as dark as the black blocks,");
  puts("respectively.  If the transition point appears to occur between two");
  puts("characters, enter both characters.");
  puts("");

  g = get_calibration("Gamma?", 1.0f, 4.0f);

 /*
  * Print pass 3 to get the RGB adjustments...
  */

  puts("");
  puts("Thank you.  Now insert the page back into the printer and press the");
  puts("ENTER key to print calibration pass #3.");
  puts("");

  safe_gets("Press ENTER to print pass #3 or N to skip...", junk, sizeof(junk));

  if (!junk[0])
  {
    puts("Sending calibration pass #3 for red, green, and blue adjustment...");

    fp = send_prolog(command);
    send_pass3(fp, kd, rd, g, yellow);
    send_trailer(fp);

    puts("Calibration pass #3 sent.");
  }

 /*
  * Get the numbers for pass #3...
  */

  puts("");
  puts("Please select the character that corresponds to the correct red,");
  puts("green, and blue colors.  If the transition point appears to occur");
  puts("between two characters, enter both characters.");
  puts("");

  red   = get_calibration("Red color?", 0.35f, -0.40f);
  green = get_calibration("Green color?", 0.35f, -0.40f);
  blue  = get_calibration("Blue color?", 0.35f, -0.40f);

 /*
  * Compute the color profile matrix...
  */

  color = 0.5f * rd / kd - kd;

  cyan    /= kd;
  magenta /= kd;
  yellow  /= kd;

  m[0][0] = cyan;			/* C */
  m[0][1] = cyan * (color + blue);	/* C + M (blue) */
  m[0][2] = cyan * (color - green);	/* C + Y (green) */
  m[1][0] = magenta * (color - blue);	/* M + C (blue) */
  m[1][1] = magenta;			/* M */
  m[1][2] = magenta * (color + red);	/* M + Y (red) */
  m[2][0] = yellow * (color + green);	/* Y + C (green) */
  m[2][1] = yellow * (color - red);	/* Y + M (red) */
  m[2][2] = yellow;			/* Y */

  if (m[0][1] > 0.0f)
  {
    m[1][0] -= m[0][1];
    m[0][1] = 0.0f;
  }
  else if (m[1][0] > 0.0f)
  {
    m[0][1] -= m[1][0];
    m[1][0] = 0.0f;
  }

  if (m[0][2] > 0.0f)
  {
    m[2][0] -= m[0][2];
    m[0][2] = 0.0f;
  }
  else if (m[2][0] > 0.0f)
  {
    m[0][2] -= m[2][0];
    m[2][0] = 0.0f;
  }

  if (m[1][2] > 0.0f)
  {
    m[2][1] -= m[1][2];
    m[1][2] = 0.0f;
  }
  else if (m[2][1] > 0.0f)
  {
    m[1][2] -= m[2][1];
    m[2][1] = 0.0f;
  }

 /*
  * Format the profiles for printing and display...
  */

  snprintf(profile, proflen,
           "%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f",
           kd * 1000.0f, g * 1000.0f,
	   m[0][0] * 1000.0f, m[0][1] * 1000.0f, m[0][2] * 1000.0f,
	   m[1][0] * 1000.0f, m[1][1] * 1000.0f, m[1][2] * 1000.0f,
	   m[2][0] * 1000.0f, m[2][1] * 1000.0f, m[2][2] * 1000.0f);

  snprintf(simpleProfile, sizeof(simpleProfile),
           "SimpleColorProfile %s/%s %.0f %.0f %.0f %.2f %.0f %.0f %.0f\n",
           resolution[0] ? resolution : "-", mediatype[0] ? mediatype : "-",
	   kd * 100.0f, yellow * kd * 100.0f, rd * 100.0f, g,
	   red * 100.0f, green * 100.0f, blue * 100.0f);

 /*
  * Print pass 4 to confirm the profile...
  */

  puts("");
  puts("Thank you.  Now insert the page back into the printer and press the");
  puts("ENTER key to print the final calibration pass.");
  puts("");

  safe_gets("Press ENTER to continue...", junk, sizeof(junk));

  puts("Sending calibration pass #4 for visual confirmation...");

  fp = send_prolog(command);
  send_pass4(fp, red, green, blue, profile - 11, simpleProfile);
  send_trailer(fp);

 /*
  * Show the final status to the user...
  */

  puts("Calibration pass #4 sent.");

  puts("");
  puts("The simple color profile for these values is:");
  puts("");
  printf("    %s\n", simpleProfile);
  puts("");
  puts("To use this profile for a single job, add the following option:");
  puts("");
  printf("    %s\n", profile - 11);
  puts("");
  puts("Calibration is complete.");

  return (0);
}


/*
 * 'get_calibration()' - Get a calibration value from the user.
 */

float					/* O - Calibration value */
get_calibration(const char  *prompt,	/* I - Prompt for user */
                float       minval,	/* I - Minimum value on scale */
		float       maxval)	/* I - Maximum value on scale */
{
  char	line[4];			/* Input from user */
  int	val1, val2;			/* Calibration values */


 /*
  * Wait for valid input from the user...
  */

  do
  {
    if (safe_gets(prompt, line, sizeof(line)) == NULL)
      return (minval);
  }
  while (!isxdigit(line[0]) || (line[1] && !isxdigit(line[1])));

 /*
  * Get the value for each digit entered...
  */

  if (isdigit(line[0]))
    val1 = line[0] - '0';
  else
    val1 = tolower(line[0]) - 'a' + 10;

  if (!line[1])
    val2 = val1;
  else if (isdigit(line[1]))
    val2 = line[1] - '0';
  else
    val2 = tolower(line[1]) - 'a' + 10;

 /*
  * Return the average value...
  */

  return (minval + (maxval - minval) * (val1 + val2) / 30.0f);
}


/*
 * 'safe_gets()' - Get a string from the user.
 */

char *					/* O - String from user or NULL */
safe_gets(const char *prompt,		/* I - Prompt for user */
          char       *s,		/* I - String buffer */
          int        size)		/* I - Size of buffer */
{
 /*
  * Show the prompt...
  */

  printf("%s ", prompt);

 /*
  * Get a string from the user...
  */

  if (fgets(s, size, stdin) == NULL)
    return (NULL);

 /*
  * Strip the trailing newline...
  */

  if (s[0])
    s[strlen(s) - 1] = '\0';

 /*
  * Return the string...
  */
 
  return (s);
}


/*
 * 'send_prolog()' - Send the standard PostScript prolog.
 */

FILE *					/* O - File to write to */
send_prolog(const char *command)	/* I - Print command */
{
  FILE	*fp;				/* File to write to */


  if (getenv("CUPS_PROFILENAME") != NULL)
    fp = fopen(getenv("CUPS_PROFILENAME"), "w");
  else
    fp = popen(command, "w");

  if (!fp)
  {
    printf("ERROR: Unable to send print job: %s\n", strerror(errno));
    return (NULL);
  }

  fputs("%!PS-Adobe-3.0\n", fp);
  fputs("%%BoundingBox: 36 72 576 720\n", fp);
  fputs("%%Pages: 1\n", fp);
  fputs("%%EndComments\n", fp);
  fputs("%%BeginProlog\n", fp);
  fputs("/BLOCK {\n"
	"	14.4 mul neg 720 add exch\n"
	"	14.4 mul 72 add exch\n"
	"	14.4 14.4 rectfill\n"
	"} bind def\n", fp);
  fputs("/DIAMOND {\n"
	"	14.4 mul neg 720 add 7.2 add exch\n"
	"	14.4 mul 72 add exch\n"
	"	newpath\n"
	"	moveto\n"
	"	7.2 7.2 rlineto\n"
	"	7.2 -7.2 rlineto\n"
	"	-7.2 -7.2 rlineto\n"
	"	closepath\n"
	"	0 ne { fill } { stroke } ifelse\n"
	"} bind def\n", fp);
  fputs("/PLUS {\n"
	"	14.4 mul neg 720 add 7.2 add exch\n"
	"	14.4 mul 72 add exch\n"
	"	newpath\n"
	"	moveto\n"
	"	14.4 0 rlineto\n"
	"	-7.2 -7.2 rmoveto\n"
	"	0 14.4 rlineto\n"
	"	closepath\n"
	"	fill\n"
	"} bind def\n", fp);
  fputs("/TEXT {\n"
        "	14.4 mul neg 720 add 4 add exch\n"
	"	14.4 mul 72 add 7.2 add exch\n"
	"	moveto\n"
	"	dup stringwidth pop 0.5 mul neg 0 rmoveto\n"
	"	show\n"
	"} bind def\n", fp);
  fputs("%%EndProlog\n", fp);
  fputs("%%Page: 1 1\n", fp);
  fputs("/Helvetica findfont 12 scalefont setfont\n", fp);

  return (fp);
}


/*
 * 'send_pass1()' - Send the first pass to set the densities.
 */

void
send_pass1(FILE *fp)			/* I - File to write to */
{
  int			i;		/* Looping var */
  float			p;		/* Color value */


  if (!fp)
    return;

 /*
  * Draw black, yellow, and red squares in decreasing densities...
  */

  fputs("0 setgray", fp);
  fputs("(K) 0 1 TEXT\n", fp);
  fputs("(Y) 0 2 TEXT\n", fp);
  fputs("(R) 0 4 TEXT\n", fp);

  for (i = 0; i < 16; i ++)
  {
    fprintf(fp, "(%d) %d 0 TEXT\n", 100 - i * 5, i * 2 + 2);
    fprintf(fp, "(%c) %d 3 TEXT\n", calhex[i], i * 2 + 2);
    fprintf(fp, "(%d) %d 5 TEXT\n", 200 - i * 10, i * 2 + 2);
  }

  for (i = 0; i < 16; i ++)
  {
    p = 0.01f * (100 - i * 5);

    fprintf(fp, "%.2f setgray %d 1 BLOCK\n", 1.0 - p, i * 2 + 2);
    fprintf(fp, "0 0 %.2f 0 setcmykcolor %d 2 BLOCK\n", p, i * 2 + 2);
    fprintf(fp, "0 %.2f %.2f 0 setcmykcolor %d 4 BLOCK\n", p, p, i * 2 + 2);
  }
}


/*
 * 'send_pass2()' - Send pass 2 to set the gamma correction.
 */

void
send_pass2(FILE  *fp,			/* I - File to write to */
	   float kd,			/* I - Black density */
	   float rd,			/* I - Red density */
	   float yellow)		/* I - Yellow density */
{
  int			i;		/* Looping var */
  float			p;		/* Color value */
  float			g;		/* Gamma value */


  if (!fp)
    return;

 /*
  * Cut red density in half...
  */

  rd *= 0.5f;

 /*
  * Mark choices on the page...
  */

  fprintf(fp, "0 0 %.2f 0 setcmykcolor\n", yellow);
  fprintf(fp, "1 %.2f 6 DIAMOND\n", 2.0f + 30.0f * (1.0f - yellow) / 0.75f);

  fprintf(fp, "0 %.2f %.2f 0 setcmykcolor\n", rd, rd);
  fprintf(fp, "%d %.2f 6 DIAMOND\n", rd != yellow,
          2.0f + 30.0f * (1.0f - rd) / 0.75f);

  p = 1.0f - kd;
  fprintf(fp, "%.2f setgray\n", p);

  if (kd == rd && kd == yellow)
    fprintf(fp, "%.2f 6 PLUS\n", 2.0f + 30.0f * (1.0f - kd) / 0.75f);
  else
    fprintf(fp, "%d %.2f 6 DIAMOND\n", kd != yellow && kd != rd,
            2.0f + 30.0f * (1.0f - kd) / 0.75f);

 /*
  * Show gamma blocks...
  */

  fputs("(100%) 0 9 TEXT\n", fp);
  fputs("(50%) 0 10 TEXT\n", fp);
  fputs("(25%) 0 11 TEXT\n", fp);

  for (i = 0; i < 16; i ++)
  {
    fprintf(fp, "(%.1f) %d 8 TEXT\n", 0.2f * (20 - i), i * 2 + 2);
    fprintf(fp, "(%c) %d 12 TEXT\n", calhex[i], i * 2 + 2);
  }

  for (i = 0; i < 16; i ++)
  {
    g = 0.2f * (20 - i);

    p = 1.0f - kd * (float)pow(1.0f, g);
    fprintf(fp, "%.2f setgray %d 9 BLOCK\n", p, i * 2 + 2);

    p = 1.0f - kd * (float)pow(0.5f, g);
    fprintf(fp, "%.2f setgray %d 10 BLOCK\n", p, i * 2 + 2);

    p = 1.0f - kd * (float)pow(0.25f, g);
    fprintf(fp, "%.2f setgray %d 11 BLOCK\n", p, i * 2 + 2);
  }
}


/*
 * 'send_pass3()' - Show pass 3 to set the color adjustments.
 */

void
send_pass3(FILE  *fp,			/* I - File to write to */
	   float kd,			/* I - Black density */
	   float rd,			/* I - Red density */
	   float g,			/* I - Gamma correction */
	   float yellow)		/* I - Yellow density */
{
  int	i;				/* Looping var */
  float p;				/* Color value */
  float	color;				/* Two-color adjustment */
  float	c, m, y;			/* CMY values */
  float	adj;				/* Adjustment value */


  if (!fp)
    return;

 /*
  * Show the selected gamma value...
  */

  p = 1.0f - kd;
  fprintf(fp, "%.2f setgray\n", p);
  fprintf(fp, "1 %.2f 13 DIAMOND\n", 2.0f + 30.0f * (4.0f - g) / 3.0f);

 /*
  * Show RGB color calibration blocks...
  */

  color  = 0.5f * rd / kd - kd;
  yellow /= kd;

  fputs("(R) 2 16 TEXT\n", fp);
  fputs("(G) 2 17 TEXT\n", fp);
  fputs("(B) 2 18 TEXT\n", fp);

  for (i = 1; i < 16; i ++)
  {
    fprintf(fp, "(%+d) %d 15 TEXT\n", i * 5 - 40, i * 2 + 2);
    fprintf(fp, "(%c) %d 19 TEXT\n", calhex[i], i * 2 + 2);
  }

  for (i = 1; i < 16; i ++)
  {
   /* Adjustment value */
    adj = i * 0.05f - 0.40f;

   /* RED */
    c = 0.0f;
    m = color + adj;
    y = color - adj;
    if (m > 0)
    {
      y -= m;
      m = 0;
    }
    else if (y > 0)
    {
      m -= y;
      y = 0;
    }

    m += 1.0f;
    y = yellow * (1.0f + y);

    if (c <= 0.0f)
      c = 0.0f;
    else if (c >= 1.0f)
      c = 1.0f;
    else
      c = (float)pow(c, g);

    if (m <= 0.0f)
      m = 0.0f;
    else if (m >= 1.0f)
      m = 1.0f;
    else
      m = (float)pow(m, g);

    if (y <= 0.0f)
      y = 0.0f;
    else if (y >= 1.0f)
      y = 1.0f;
    else
      y = (float)pow(y, g);

    fprintf(fp, "%.2f %.2f %.2f 0 setcmykcolor %d 16 BLOCK\n",
            c * kd, m * kd, y * kd, i * 2 + 2);

   /* GREEN */
    c = color - adj;
    m = 0.0f;
    y = color + adj;

    if (c > 0)
    {
      y -= c;
      c = 0;
    }
    else if (y > 0)
    {
      c -= y;
      y = 0;
    }

    c += 1.0f;
    y = yellow * (1.0f + y);

    if (c <= 0.0f)
      c = 0.0f;
    else if (c >= 1.0f)
      c = 1.0f;
    else
      c = (float)pow(c, g);

    if (m <= 0.0f)
      m = 0.0f;
    else if (m >= 1.0f)
      m = 1.0f;
    else
      m = (float)pow(m, g);

    if (y <= 0.0f)
      y = 0.0f;
    else if (y >= 1.0f)
      y = 1.0f;
    else
      y = (float)pow(y, g);

    fprintf(fp, "%.2f %.2f %.2f 0 setcmykcolor %d 17 BLOCK\n",
            c * kd, m * kd, y * kd, i * 2 + 2);

   /* BLUE */
    c = color + adj;
    m = color - adj;
    y = 0.0f;

    if (c > 0)
    {
      m -= c;
      c = 0;
    }
    else if (m > 0)
    {
      c -= m;
      m = 0;
    }

    c += 1.0f;
    m += 1.0f;

    if (c <= 0.0f)
      c = 0.0f;
    else if (c >= 1.0f)
      c = 1.0f;
    else
      c = (float)pow(c, g);

    if (m <= 0.0f)
      m = 0.0f;
    else if (m >= 1.0f)
      m = 1.0f;
    else
      m = (float)pow(m, g);

    if (y <= 0.0f)
      y = 0.0f;
    else if (y >= 1.0f)
      y = 1.0f;
    else
      y = (float)pow(y, g);

    fprintf(fp, "%.2f %.2f %.2f 0 setcmykcolor %d 18 BLOCK\n",
            c * kd, m * kd, y * kd, i * 2 + 2);
  }
}


/*
 * 'send_pass4()' - Send the 4th and final pass with the test image.
 */

void
send_pass4(FILE       *fp,		/* I - File to write to */
	   float      red,		/* I - Red adjustment */
	   float      green,		/* I - Green adjustment */
	   float      blue,		/* I - Blue adjustment */
	   const char *profile,		/* I - -o profile=... */
	   const char *simpleProfile)	/* I - SimpleColorProfile */
{
  FILE	*ppm;				/* Test image */
  int	x, y,				/* Position in image */
	col,				/* Column in line */
	width, height;			/* Dimensions of test image */
  int	r, g, b;			/* Current RGB color */
  char	line[255];			/* Header line from image file */


  if (!fp)
    return;

 /*
  * Show RGB adjustment settings...
  */

  fprintf(fp, "0 0 1 setrgbcolor 1 %.2f 20 DIAMOND\n", /* BLUE */
          2.0f + 30.0f * (0.40f + blue) / 0.75f);
  fprintf(fp, "1 0 0 setrgbcolor %d %.2f 20 DIAMOND\n", /* RED */
          red != blue, 2.0f + 30.0f * (0.40f + red) / 0.75f);
  if (green == red && green == blue)
    fprintf(fp, "0 1 0 setrgbcolor %.2f 20 PLUS\n", /* GREEN */
            2.0f + 30.0f * (0.40f + green) / 0.75f);
  else
    fprintf(fp, "0 1 0 setrgbcolor %d %.2f 20 DIAMOND\n", /* GREEN */
            green != red && green != blue,
	    2.0f + 30.0f * (0.40f + green) / 0.75f);

 /*
  * Show color profile...
  */

  fputs("0 setgray\n", fp);
  fputs("currentfont 0.8 scalefont setfont\n", fp);

  fprintf(fp, "(%s) 16 22 TEXT\n", profile);
  fprintf(fp, "(%s) 16 23 TEXT\n", simpleProfile);

 /*
  * Show the test image...
  */

  if ((ppm = fopen(DDK_DATADIR "/cupsprofile.ppm", "rb")) == NULL)
    if ((ppm = fopen("cupsprofile.ppm", "rb")) == NULL)
    {
      printf("ERROR: Unable to open test image file \"" DDK_DATADIR
             "/cupsprofile.ppm\":\n    %s\n", strerror(errno));
      return;
    }

 /*
  * Skip all comment lines...
  */

  fgets(line, sizeof(line), ppm);
  while (fgets(line, sizeof(line), ppm))
    if (line[0] != '#')
      break;

 /*
  * Read the width and height...
  */

  sscanf(line, "%d%d", &width, &height);

 /*
  * Skip the max value line...
  */

  fgets(line, sizeof(line), ppm);

  fputs("gsave\n", fp);
  fprintf(fp, "72 %d translate\n", 504 * height / width + 144);
  fprintf(fp, "504 -%d scale\n", 504 * height / width);

  fprintf(fp, "/scanline %d string def\n", width * 3);

  fprintf(fp, "%d %d 8\n", width, height);
  fprintf(fp, "[%d 0 0 %d 0 0]\n", width, height);
  fputs("{ currentfile scanline readhexstring pop } false 3 colorimage\n", fp);

  for (y = 0, col = 0; y < height; y ++)
  {
    printf("Sending scanline %d of %d...\r", y + 1, height);
    fflush(stdout);

    for (x = 0; x < width; x ++)
    {
      r = getc(ppm);
      g = getc(ppm);
      b = getc(ppm);

      fprintf(fp, "%02X%02X%02X", r, g, b);
      col ++;
      if (col >= 12)
      {
        col = 0;
	putc('\n', fp);
      }
    }
  }

  if (col)
    putc('\n', fp);

  fputs("grestore\n", fp);

  printf("                                    \r");

 /*
  * Close the image file and return...
  */

  fclose(ppm);
}


/*
 * 'send_trailer()' - Send the standard PostScript trailer.
 */

int					/* O - Close status */
send_trailer(FILE *fp)			/* I - File to write to */
{
  if (!fp)
    return (-1);

 /*
  * Write the showpage and EOF marker...
  */

  fputs("showpage\n", fp);
  fputs("%%EOF\n", fp);

 /*
  * Close the file or command pipe...
  */

  if (getenv("CUPS_PROFILENAME") != NULL)
    return (fclose(fp));
  else
    return (pclose(fp));
}


/*
 * End of "$Id: cupsprofile.c 343 2007-07-13 19:52:48Z mike $".
 */
