/*
 * Page size functions for CUPS.
 *
 * Copyright 2007-2015 by Apple Inc.
 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
 *
 * 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/".
 *
 * PostScript is a trademark of Adobe Systems, Inc.
 *
 * This file is subject to the Apple OS-Developed Software exception.
 */

/*
 * Include necessary headers...
 */

#include "string-private.h"
#include "debug-private.h"
#include "ppd.h"


/*
 * 'ppdPageSize()' - Get the page size record for the named size.
 */

ppd_size_t *				/* O - Size record for page or NULL */
ppdPageSize(ppd_file_t *ppd,		/* I - PPD file record */
            const char *name)		/* I - Size name */
{
  int		i;			/* Looping var */
  ppd_size_t	*size;			/* Current page size */
  double	w, l;			/* Width and length of page */
  char		*nameptr;		/* Pointer into name */
  struct lconv	*loc;			/* Locale data */
  ppd_coption_t	*coption;		/* Custom option for page size */
  ppd_cparam_t	*cparam;		/* Custom option parameter */


  DEBUG_printf(("2ppdPageSize(ppd=%p, name=\"%s\")", ppd, name));

  if (!ppd)
  {
    DEBUG_puts("3ppdPageSize: Bad PPD pointer, returning NULL...");
    return (NULL);
  }

  if (name)
  {
    if (!strncmp(name, "Custom.", 7) && ppd->variable_sizes)
    {
     /*
      * Find the custom page size...
      */

      for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
	if (!strcmp("Custom", size->name))
          break;

      if (!i)
      {
	DEBUG_puts("3ppdPageSize: No custom sizes, returning NULL...");
        return (NULL);
      }

     /*
      * Variable size; size name can be one of the following:
      *
      *    Custom.WIDTHxLENGTHin    - Size in inches
      *    Custom.WIDTHxLENGTHft    - Size in feet
      *    Custom.WIDTHxLENGTHcm    - Size in centimeters
      *    Custom.WIDTHxLENGTHmm    - Size in millimeters
      *    Custom.WIDTHxLENGTHm     - Size in meters
      *    Custom.WIDTHxLENGTH[pt]  - Size in points
      */

      loc = localeconv();
      w   = _cupsStrScand(name + 7, &nameptr, loc);
      if (!nameptr || *nameptr != 'x')
        return (NULL);

      l = _cupsStrScand(nameptr + 1, &nameptr, loc);
      if (!nameptr)
        return (NULL);

      if (!_cups_strcasecmp(nameptr, "in"))
      {
        w *= 72.0;
	l *= 72.0;
      }
      else if (!_cups_strcasecmp(nameptr, "ft"))
      {
        w *= 12.0 * 72.0;
	l *= 12.0 * 72.0;
      }
      else if (!_cups_strcasecmp(nameptr, "mm"))
      {
        w *= 72.0 / 25.4;
        l *= 72.0 / 25.4;
      }
      else if (!_cups_strcasecmp(nameptr, "cm"))
      {
        w *= 72.0 / 2.54;
        l *= 72.0 / 2.54;
      }
      else if (!_cups_strcasecmp(nameptr, "m"))
      {
        w *= 72.0 / 0.0254;
        l *= 72.0 / 0.0254;
      }

      size->width  = (float)w;
      size->length = (float)l;
      size->left   = ppd->custom_margins[0];
      size->bottom = ppd->custom_margins[1];
      size->right  = (float)(w - ppd->custom_margins[2]);
      size->top    = (float)(l - ppd->custom_margins[3]);

     /*
      * Update the custom option records for the page size, too...
      */

      if ((coption = ppdFindCustomOption(ppd, "PageSize")) != NULL)
      {
        if ((cparam = ppdFindCustomParam(coption, "Width")) != NULL)
	  cparam->current.custom_points = (float)w;

        if ((cparam = ppdFindCustomParam(coption, "Height")) != NULL)
	  cparam->current.custom_points = (float)l;
      }

     /*
      * Return the page size...
      */

      DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
                    size->name, size->width, size->length));

      return (size);
    }
    else
    {
     /*
      * Lookup by name...
      */

      for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
	if (!_cups_strcasecmp(name, size->name))
	{
	  DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
			size->name, size->width, size->length));

          return (size);
	}
    }
  }
  else
  {
   /*
    * Find default...
    */

    for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
      if (size->marked)
      {
	DEBUG_printf(("3ppdPageSize: Returning %p (\"%s\", %gx%g)", size,
		      size->name, size->width, size->length));

        return (size);
      }
  }

  DEBUG_puts("3ppdPageSize: Size not found, returning NULL");

  return (NULL);
}


/*
 * 'ppdPageSizeLimits()' - Return the custom page size limits.
 *
 * This function returns the minimum and maximum custom page sizes and printable
 * areas based on the currently-marked (selected) options.
 *
 * If the specified PPD file does not support custom page sizes, both
 * "minimum" and "maximum" are filled with zeroes.
 *
 * @since CUPS 1.4/macOS 10.6@
 */

int					/* O - 1 if custom sizes are supported, 0 otherwise */
ppdPageSizeLimits(ppd_file_t *ppd,	/* I - PPD file record */
                  ppd_size_t *minimum,	/* O - Minimum custom size */
		  ppd_size_t *maximum)	/* O - Maximum custom size */
{
  ppd_choice_t	*qualifier2,		/* Second media qualifier */
		*qualifier3;		/* Third media qualifier */
  ppd_attr_t	*attr;			/* Attribute */
  float		width,			/* Min/max width */
		length;			/* Min/max length */
  char		spec[PPD_MAX_NAME];	/* Selector for min/max */


 /*
  * Range check input...
  */

  if (!ppd || !ppd->variable_sizes || !minimum || !maximum)
  {
    if (minimum)
      memset(minimum, 0, sizeof(ppd_size_t));

    if (maximum)
      memset(maximum, 0, sizeof(ppd_size_t));

    return (0);
  }

 /*
  * See if we have the cupsMediaQualifier2 and cupsMediaQualifier3 attributes...
  */

  cupsArraySave(ppd->sorted_attrs);

  if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier2", NULL)) != NULL &&
      attr->value)
    qualifier2 = ppdFindMarkedChoice(ppd, attr->value);
  else
    qualifier2 = NULL;

  if ((attr = ppdFindAttr(ppd, "cupsMediaQualifier3", NULL)) != NULL &&
      attr->value)
    qualifier3 = ppdFindMarkedChoice(ppd, attr->value);
  else
    qualifier3 = NULL;

 /*
  * Figure out the current minimum width and length...
  */

  width  = ppd->custom_min[0];
  length = ppd->custom_min[1];

  if (qualifier2)
  {
   /*
    * Try getting cupsMinSize...
    */

    if (qualifier3)
    {
      snprintf(spec, sizeof(spec), ".%s.%s", qualifier2->choice,
	       qualifier3->choice);
      attr = ppdFindAttr(ppd, "cupsMinSize", spec);
    }
    else
      attr = NULL;

    if (!attr)
    {
      snprintf(spec, sizeof(spec), ".%s.", qualifier2->choice);
      attr = ppdFindAttr(ppd, "cupsMinSize", spec);
    }

    if (!attr && qualifier3)
    {
      snprintf(spec, sizeof(spec), "..%s", qualifier3->choice);
      attr = ppdFindAttr(ppd, "cupsMinSize", spec);
    }

    if ((attr && attr->value &&
         sscanf(attr->value, "%f%f", &width, &length) != 2) || !attr)
    {
      width  = ppd->custom_min[0];
      length = ppd->custom_min[1];
    }
  }

  minimum->width  = width;
  minimum->length = length;
  minimum->left   = ppd->custom_margins[0];
  minimum->bottom = ppd->custom_margins[1];
  minimum->right  = width - ppd->custom_margins[2];
  minimum->top    = length - ppd->custom_margins[3];

 /*
  * Figure out the current maximum width and length...
  */

  width  = ppd->custom_max[0];
  length = ppd->custom_max[1];

  if (qualifier2)
  {
   /*
    * Try getting cupsMaxSize...
    */

    if (qualifier3)
    {
      snprintf(spec, sizeof(spec), ".%s.%s", qualifier2->choice,
	       qualifier3->choice);
      attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
    }
    else
      attr = NULL;

    if (!attr)
    {
      snprintf(spec, sizeof(spec), ".%s.", qualifier2->choice);
      attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
    }

    if (!attr && qualifier3)
    {
      snprintf(spec, sizeof(spec), "..%s", qualifier3->choice);
      attr = ppdFindAttr(ppd, "cupsMaxSize", spec);
    }

    if (!attr ||
        (attr->value && sscanf(attr->value, "%f%f", &width, &length) != 2))
    {
      width  = ppd->custom_max[0];
      length = ppd->custom_max[1];
    }
  }

  maximum->width  = width;
  maximum->length = length;
  maximum->left   = ppd->custom_margins[0];
  maximum->bottom = ppd->custom_margins[1];
  maximum->right  = width - ppd->custom_margins[2];
  maximum->top    = length - ppd->custom_margins[3];

 /*
  * Return the min and max...
  */

  cupsArrayRestore(ppd->sorted_attrs);

  return (1);
}


/*
 * 'ppdPageWidth()' - Get the page width for the given size.
 */

float				/* O - Width of page in points or 0.0 */
ppdPageWidth(ppd_file_t *ppd,	/* I - PPD file record */
             const char *name)	/* I - Size name */
{
  ppd_size_t	*size;		/* Page size */


  if ((size = ppdPageSize(ppd, name)) == NULL)
    return (0.0);
  else
    return (size->width);
}


/*
 * 'ppdPageLength()' - Get the page length for the given size.
 */

float				/* O - Length of page in points or 0.0 */
ppdPageLength(ppd_file_t *ppd,	/* I - PPD file */
              const char *name)	/* I - Size name */
{
  ppd_size_t	*size;		/* Page size */


  if ((size = ppdPageSize(ppd, name)) == NULL)
    return (0.0);
  else
    return (size->length);
}