Blob Blame History Raw
/*
 * Common filter routines for CUPS.
 *
 * Copyright 2007-2014 by Apple Inc.
 * Copyright 1997-2006 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
 * missing or damaged, see the license at "http://www.cups.org/".
 *
 * This file is subject to the Apple OS-Developed Software exception.
 */

/*
 * Include necessary headers...
 */

#include "config.h"
#ifdef WITH_LSPP
#define _GNU_SOURCE
#include <string.h>
#endif /* WITH_LSPP */

#include "common.h"
#include <locale.h>


/*
 * Globals...
 */

int	Orientation = 0,		/* 0 = portrait, 1 = landscape, etc. */
	Duplex = 0,			/* Duplexed? */
	LanguageLevel = 1,		/* Language level of printer */
	ColorDevice = 1;		/* Do color text? */
float	PageLeft = 18.0f,		/* Left margin */
	PageRight = 594.0f,		/* Right margin */
	PageBottom = 36.0f,		/* Bottom margin */
	PageTop = 756.0f,		/* Top margin */
	PageWidth = 612.0f,		/* Total page width */
	PageLength = 792.0f;		/* Total page length */


/*
 * 'SetCommonOptions()' - Set common filter options for media size, etc.
 */

ppd_file_t *				/* O - PPD file */
SetCommonOptions(
    int           num_options,		/* I - Number of options */
    cups_option_t *options,		/* I - Options */
    int           change_size)		/* I - Change page size? */
{
  ppd_file_t	*ppd;			/* PPD file */
  ppd_size_t	*pagesize;		/* Current page size */
  const char	*val;			/* Option value */


#ifdef LC_TIME
  setlocale(LC_TIME, "");
#endif /* LC_TIME */

  ppd = ppdOpenFile(getenv("PPD"));

  ppdMarkDefaults(ppd);
  cupsMarkOptions(ppd, num_options, options);

  if ((pagesize = ppdPageSize(ppd, NULL)) != NULL)
  {
    PageWidth  = pagesize->width;
    PageLength = pagesize->length;
    PageTop    = pagesize->top;
    PageBottom = pagesize->bottom;
    PageLeft   = pagesize->left;
    PageRight  = pagesize->right;

    fprintf(stderr, "DEBUG: Page = %.0fx%.0f; %.0f,%.0f to %.0f,%.0f\n",
            PageWidth, PageLength, PageLeft, PageBottom, PageRight, PageTop);
  }

  if (ppd != NULL)
  {
    ColorDevice   = ppd->color_device;
    LanguageLevel = ppd->language_level;
  }

  if ((val = cupsGetOption("landscape", num_options, options)) != NULL)
  {
    if (_cups_strcasecmp(val, "no") != 0 && _cups_strcasecmp(val, "off") != 0 &&
        _cups_strcasecmp(val, "false") != 0)
    {
      if (ppd && ppd->landscape > 0)
        Orientation = 1;
      else
        Orientation = 3;
    }
  }
  else if ((val = cupsGetOption("orientation-requested", num_options, options)) != NULL)
  {
   /*
    * Map IPP orientation values to 0 to 3:
    *
    *   3 = 0 degrees   = 0
    *   4 = 90 degrees  = 1
    *   5 = -90 degrees = 3
    *   6 = 180 degrees = 2
    */

    Orientation = atoi(val) - 3;
    if (Orientation >= 2)
      Orientation ^= 1;
  }

  if ((val = cupsGetOption("page-left", num_options, options)) != NULL)
  {
    switch (Orientation & 3)
    {
      case 0 :
          PageLeft = (float)atof(val);
	  break;
      case 1 :
          PageBottom = (float)atof(val);
	  break;
      case 2 :
          PageRight = PageWidth - (float)atof(val);
	  break;
      case 3 :
          PageTop = PageLength - (float)atof(val);
	  break;
    }
  }

  if ((val = cupsGetOption("page-right", num_options, options)) != NULL)
  {
    switch (Orientation & 3)
    {
      case 0 :
          PageRight = PageWidth - (float)atof(val);
	  break;
      case 1 :
          PageTop = PageLength - (float)atof(val);
	  break;
      case 2 :
          PageLeft = (float)atof(val);
	  break;
      case 3 :
          PageBottom = (float)atof(val);
	  break;
    }
  }

  if ((val = cupsGetOption("page-bottom", num_options, options)) != NULL)
  {
    switch (Orientation & 3)
    {
      case 0 :
          PageBottom = (float)atof(val);
	  break;
      case 1 :
          PageLeft = (float)atof(val);
	  break;
      case 2 :
          PageTop = PageLength - (float)atof(val);
	  break;
      case 3 :
          PageRight = PageWidth - (float)atof(val);
	  break;
    }
  }

  if ((val = cupsGetOption("page-top", num_options, options)) != NULL)
  {
    switch (Orientation & 3)
    {
      case 0 :
          PageTop = PageLength - (float)atof(val);
	  break;
      case 1 :
          PageRight = PageWidth - (float)atof(val);
	  break;
      case 2 :
          PageBottom = (float)atof(val);
	  break;
      case 3 :
          PageLeft = (float)atof(val);
	  break;
    }
  }

  if (change_size)
    UpdatePageVars();

  if (ppdIsMarked(ppd, "Duplex", "DuplexNoTumble") ||
      ppdIsMarked(ppd, "Duplex", "DuplexTumble") ||
      ppdIsMarked(ppd, "JCLDuplex", "DuplexNoTumble") ||
      ppdIsMarked(ppd, "JCLDuplex", "DuplexTumble") ||
      ppdIsMarked(ppd, "EFDuplex", "DuplexNoTumble") ||
      ppdIsMarked(ppd, "EFDuplex", "DuplexTumble") ||
      ppdIsMarked(ppd, "KD03Duplex", "DuplexNoTumble") ||
      ppdIsMarked(ppd, "KD03Duplex", "DuplexTumble"))
    Duplex = 1;

  return (ppd);
}


/*
 * 'UpdatePageVars()' - Update the page variables for the orientation.
 */

void
UpdatePageVars(void)
{
  float		temp;			/* Swapping variable */


  switch (Orientation & 3)
  {
    case 0 : /* Portait */
        break;

    case 1 : /* Landscape */
	temp       = PageLeft;
	PageLeft   = PageBottom;
	PageBottom = temp;

	temp       = PageRight;
	PageRight  = PageTop;
	PageTop    = temp;

	temp       = PageWidth;
	PageWidth  = PageLength;
	PageLength = temp;
	break;

    case 2 : /* Reverse Portrait */
	temp       = PageWidth - PageLeft;
	PageLeft   = PageWidth - PageRight;
	PageRight  = temp;

	temp       = PageLength - PageBottom;
	PageBottom = PageLength - PageTop;
	PageTop    = temp;
        break;

    case 3 : /* Reverse Landscape */
	temp       = PageWidth - PageLeft;
	PageLeft   = PageWidth - PageRight;
	PageRight  = temp;

	temp       = PageLength - PageBottom;
	PageBottom = PageLength - PageTop;
	PageTop    = temp;

	temp       = PageLeft;
	PageLeft   = PageBottom;
	PageBottom = temp;

	temp       = PageRight;
	PageRight  = PageTop;
	PageTop    = temp;

	temp       = PageWidth;
	PageWidth  = PageLength;
	PageLength = temp;
	break;
  }
}


/*
 * 'WriteCommon()' - Write common procedures...
 */

void
WriteCommon(void)
{
  puts("% x y w h ESPrc - Clip to a rectangle.\n"
       "userdict/ESPrc/rectclip where{pop/rectclip load}\n"
       "{{newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
       "neg 0 rlineto closepath clip newpath}bind}ifelse put");
  puts("% x y w h ESPrf - Fill a rectangle.\n"
       "userdict/ESPrf/rectfill where{pop/rectfill load}\n"
       "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
       "neg 0 rlineto closepath fill grestore}bind}ifelse put");
  puts("% x y w h ESPrs - Stroke a rectangle.\n"
       "userdict/ESPrs/rectstroke where{pop/rectstroke load}\n"
       "{{gsave newpath 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto\n"
       "neg 0 rlineto closepath stroke grestore}bind}ifelse put");
}


/*
 * 'WriteLabelProlog()' - Write the prolog with the classification
 *                        and page label.
 */

void
WriteLabelProlog(const char *label,	/* I - Page label */
		 float      bottom,	/* I - Bottom position in points */
		 float      top,	/* I - Top position in points */
		 float      width)	/* I - Width in points */
{
  const char	*classification;	/* CLASSIFICATION environment variable */
  const char	*ptr;			/* Temporary string pointer */
#ifdef WITH_LSPP
  int           i,                      /* counter */
                n,                      /* counter */
                lines,                  /* number of lines needed */
                line_len,               /* index into tmp_label */
                label_len,              /* length of the label in characters */
                label_index,            /* index into the label */
                longest,                /* length of the longest line */
                longest_line,           /* index to the longest line */
                max_width;              /* maximum width in characters */
  char          **wrapped_label;        /* label with line breaks */
#endif /* WITH_LSPP */


 /*
  * First get the current classification...
  */

  if ((classification = getenv("CLASSIFICATION")) == NULL)
    classification = "";
  if (strcmp(classification, "none") == 0)
    classification = "";

 /*
  * If there is nothing to show, bind an empty 'write labels' procedure
  * and return...
  */

  if (!classification[0] && (label == NULL || !label[0]))
  {
    puts("userdict/ESPwl{}bind put");
    return;
  }

#ifdef WITH_LSPP
  if (strncmp(classification, "LSPP:", 5) == 0 && label == NULL)
  {
   /*
    * Based on the 12pt fixed width font below determine the max_width
    */
    max_width = width / 8;
    longest_line = 0;
    longest = 0;
    classification += 5; // Skip the "LSPP:"
    label_len = strlen(classification);

    if (label_len > max_width)
    {
      lines = 1 + (int)(label_len / max_width);
      line_len = (int)(label_len / lines);
      wrapped_label = malloc(sizeof(*wrapped_label) * lines);
      label_index = i = n = 0;
      while (classification[label_index])
      {
        if ((label_index + line_len) > label_len)
          break;
        switch (classification[label_index + line_len + i])
        {
          case ':':
          case ',':
          case '-':
            i++;
            wrapped_label[n++] = strndup(&classification[label_index], (line_len + i));
            label_index += line_len + i;
            i = 0;
            break;
          default:
            i++;
            break;
        }
        if ((i + line_len) == max_width)
        {
          wrapped_label[n++] = strndup(&(classification[label_index]), (line_len + i));
          label_index = label_index + line_len + i;
          i = 0;
        }
      }
      wrapped_label[n] = strndup(&classification[label_index], label_len - label_index);
    }
    else
    {
      lines = 1;
      wrapped_label = malloc(sizeof(*wrapped_label));
      wrapped_label[0] = (char*)classification;
    }

    for (n = 0; n < lines; n++ )
    {
      printf("userdict/ESPp%c(", ('a' + n));
      for (ptr = wrapped_label[n], i = 0; *ptr; ptr ++, i++)
        if (*ptr < 32 || *ptr > 126)
          printf("\\%03o", *ptr);
        else
        {
          if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
            putchar('\\');

          printf("%c", *ptr);
        }
      if (i > longest)
      {
        longest = i;
        longest_line = n;
      }
      printf(")put\n");
    }

   /*
    * For LSPP use a fixed width font so that line wrapping can be calculated
    */

    puts("userdict/ESPlf /Nimbus-Mono findfont 12 scalefont put");

   /*
    * Finally, the procedure to write the labels on the page...
    */

    printf("userdict/ESPwl{\n"
           "  ESPlf setfont\n");
    printf("  ESPp%c stringwidth pop dup 12 add exch -0.5 mul %.0f add\n ",
           'a' + longest_line, width * 0.5f);
    for (n = 1; n < lines; n++)
      printf(" dup");
    printf("\n  1 setgray\n");
    printf("  dup 6 sub %.0f %d index %.0f ESPrf\n",
           (bottom - 2.0), (2 + lines), 6.0 + (16.0 * lines));
    printf("  dup 6 sub %.0f %d index %.0f ESPrf\n",
           (top - 6.0 - (16.0 * lines)), (2 + lines), 4.0 + (16.0 * lines));
    printf("  0 setgray\n");
    printf("  dup 6 sub %.0f %d index %.0f ESPrs\n",
           (bottom - 2.0), (2 + lines), 6.0 + (16.0 * lines));
    printf("  dup 6 sub %.0f %d index %.0f ESPrs\n",
           (top - 6.0 - (16.0 * lines)), (2 + lines), 4.0 + (16.0 * lines));
    for (n = 0; n < lines; n ++)
    {
      printf("  dup %.0f moveto ESPp%c show\n",
             bottom + 6.0 + ((lines - (n+1)) * 16.0), 'a' + n);
      printf("  %.0f moveto ESPp%c show\n", top + 2.0 - ((n + 1) * 16.0), 'a' + n);
    }
    printf("  pop\n"
           "}bind put\n");

   /*
    * Do some clean up at the end of the LSPP special case
    */
    free(wrapped_label);

  }
  else
  {
#endif /* !WITH_LSPP */
  
 /*
  * Set the classification + page label string...
  */

  printf("userdict");
  if (strcmp(classification, "confidential") == 0)
    printf("/ESPpl(CONFIDENTIAL");
  else if (strcmp(classification, "classified") == 0)
    printf("/ESPpl(CLASSIFIED");
  else if (strcmp(classification, "secret") == 0)
    printf("/ESPpl(SECRET");
  else if (strcmp(classification, "topsecret") == 0)
    printf("/ESPpl(TOP SECRET");
  else if (strcmp(classification, "unclassified") == 0)
    printf("/ESPpl(UNCLASSIFIED");
  else
  {
    printf("/ESPpl(");

    for (ptr = classification; *ptr; ptr ++)
      if (*ptr < 32 || *ptr > 126)
        printf("\\%03o", *ptr);
      else if (*ptr == '_')
        putchar(' ');
      else
      {
	if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
	  putchar('\\');

	putchar(*ptr);
      }
  }

  if (label)
  {
    if (classification[0])
      printf(" - ");

   /*
    * Quote the label string as needed...
    */

    for (ptr = label; *ptr; ptr ++)
      if (*ptr < 32 || *ptr > 126)
        printf("\\%03o", *ptr);
      else
      {
	if (*ptr == '(' || *ptr == ')' || *ptr == '\\')
	  putchar('\\');

	putchar(*ptr);
      }
  }

  puts(")put");

 /*
  * Then get a 14 point Helvetica-Bold font...
  */

  puts("userdict/ESPpf /Helvetica-Bold findfont 14 scalefont put");

 /*
  * Finally, the procedure to write the labels on the page...
  */

  puts("userdict/ESPwl{");
  puts("  ESPpf setfont");
  printf("  ESPpl stringwidth pop dup 12 add exch -0.5 mul %.0f add\n",
         width * 0.5f);
  puts("  1 setgray");
  printf("  dup 6 sub %.0f 3 index 20 ESPrf\n", bottom - 2.0);
  printf("  dup 6 sub %.0f 3 index 20 ESPrf\n", top - 18.0);
  puts("  0 setgray");
  printf("  dup 6 sub %.0f 3 index 20 ESPrs\n", bottom - 2.0);
  printf("  dup 6 sub %.0f 3 index 20 ESPrs\n", top - 18.0);
  printf("  dup %.0f moveto ESPpl show\n", bottom + 2.0);
  printf("  %.0f moveto ESPpl show\n", top - 14.0);
  puts("pop");
  puts("}bind put");
  }
#ifdef WITH_LSPP
}
#endif /* WITH_LSPP */


/*
 * 'WriteLabels()' - Write the actual page labels.
 */

void
WriteLabels(int orient)	/* I - Orientation of the page */
{
  float	width,		/* Width of page */
	length;		/* Length of page */


  puts("gsave");

  if ((orient ^ Orientation) & 1)
  {
    width  = PageLength;
    length = PageWidth;
  }
  else
  {
    width  = PageWidth;
    length = PageLength;
  }

  switch (orient & 3)
  {
    case 1 : /* Landscape */
        printf("%.1f 0.0 translate 90 rotate\n", length);
        break;
    case 2 : /* Reverse Portrait */
        printf("%.1f %.1f translate 180 rotate\n", width, length);
        break;
    case 3 : /* Reverse Landscape */
        printf("0.0 %.1f translate -90 rotate\n", width);
        break;
  }

  puts("ESPwl");
  puts("grestore");
}


/*
 * 'WriteTextComment()' - Write a DSC text comment.
 */

void
WriteTextComment(const char *name,	/* I - Comment name ("Title", etc.) */
                 const char *value)	/* I - Comment value */
{
  int	len;				/* Current line length */


 /*
  * DSC comments are of the form:
  *
  *   %%name: value
  *
  * The name and value must be limited to 7-bit ASCII for most printers,
  * so we escape all non-ASCII and ASCII control characters as described
  * in the Adobe Document Structuring Conventions specification.
  */

  printf("%%%%%s: (", name);
  len = 5 + (int)strlen(name);

  while (*value)
  {
    if (*value < ' ' || *value >= 127)
    {
     /*
      * Escape this character value...
      */

      if (len >= 251)			/* Keep line < 254 chars */
        break;

      printf("\\%03o", *value & 255);
      len += 4;
    }
    else if (*value == '\\')
    {
     /*
      * Escape the backslash...
      */

      if (len >= 253)			/* Keep line < 254 chars */
        break;

      putchar('\\');
      putchar('\\');
      len += 2;
    }
    else
    {
     /*
      * Put this character literally...
      */

      if (len >= 254)			/* Keep line < 254 chars */
        break;

      putchar(*value);
      len ++;
    }

    value ++;
  }

  puts(")");
}