Home | History | Annotate | Download | only in filter
      1 /*
      2  * CUPS raster to PWG raster format filter for CUPS.
      3  *
      4  * Copyright 2011, 2014-2017 Apple Inc.
      5  *
      6  * These coded instructions, statements, and computer programs are the
      7  * property of Apple Inc. and are protected by Federal copyright law.
      8  * Distribution and use rights are outlined in the file "LICENSE.txt"
      9  * which should have been included with this file.  If this file is
     10  * missing or damaged, see the license at "http://www.cups.org/".
     11  *
     12  * This file is subject to the Apple OS-Developed Software exception.
     13  */
     14 
     15 /*
     16  * Include necessary headers...
     17  */
     18 
     19 #include <cups/cups-private.h>
     20 #include <cups/ppd-private.h>
     21 #include <cups/raster.h>
     22 #include <unistd.h>
     23 #include <fcntl.h>
     24 
     25 
     26 /*
     27  * 'main()' - Main entry for filter.
     28  */
     29 
     30 int					/* O - Exit status */
     31 main(int  argc,				/* I - Number of command-line args */
     32      char *argv[])			/* I - Command-line arguments */
     33 {
     34   const char		*final_content_type;
     35 					/* FINAL_CONTENT_TYPE env var */
     36   int			fd;		/* Raster file */
     37   cups_raster_t		*inras,		/* Input raster stream */
     38 			*outras;	/* Output raster stream */
     39   cups_page_header2_t	inheader,	/* Input raster page header */
     40 			outheader;	/* Output raster page header */
     41   unsigned		y;		/* Current line */
     42   unsigned char		*line;		/* Line buffer */
     43   unsigned		page = 0,	/* Current page */
     44 			page_width,	/* Actual page width */
     45 			page_height,	/* Actual page height */
     46 			page_top,	/* Top margin */
     47 			page_bottom,	/* Bottom margin */
     48 			page_left,	/* Left margin */
     49 			linesize,	/* Bytes per line */
     50 			lineoffset;	/* Offset into line */
     51   unsigned char		white;		/* White pixel */
     52   ppd_file_t		*ppd;		/* PPD file */
     53   ppd_attr_t		*back;		/* cupsBackSide attribute */
     54   _ppd_cache_t		*cache;		/* PPD cache */
     55   pwg_size_t		*pwg_size;	/* PWG media size */
     56   pwg_media_t		*pwg_media;	/* PWG media name */
     57   int	 		num_options;	/* Number of options */
     58   cups_option_t		*options = NULL;/* Options */
     59   const char		*val;		/* Option value */
     60 
     61 
     62   if (argc < 6 || argc > 7)
     63   {
     64     puts("Usage: rastertopwg job user title copies options [filename]");
     65     return (1);
     66   }
     67   else if (argc == 7)
     68   {
     69     if ((fd = open(argv[6], O_RDONLY)) < 0)
     70     {
     71       perror("ERROR: Unable to open print file");
     72       return (1);
     73     }
     74   }
     75   else
     76     fd = 0;
     77 
     78   if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
     79     final_content_type = "image/pwg-raster";
     80 
     81   inras  = cupsRasterOpen(fd, CUPS_RASTER_READ);
     82   outras = cupsRasterOpen(1, !strcmp(final_content_type, "image/pwg-raster") ? CUPS_RASTER_WRITE_PWG : CUPS_RASTER_WRITE_APPLE);
     83 
     84   ppd   = ppdOpenFile(getenv("PPD"));
     85   back  = ppdFindAttr(ppd, "cupsBackSide", NULL);
     86 
     87   num_options = cupsParseOptions(argv[5], 0, &options);
     88 
     89   ppdMarkDefaults(ppd);
     90   cupsMarkOptions(ppd, num_options, options);
     91 
     92   cache = ppd ? ppd->cache : NULL;
     93 
     94   while (cupsRasterReadHeader2(inras, &inheader))
     95   {
     96    /*
     97     * Show page device dictionary...
     98     */
     99 
    100     fprintf(stderr, "DEBUG: Duplex = %d\n", inheader.Duplex);
    101     fprintf(stderr, "DEBUG: HWResolution = [ %d %d ]\n", inheader.HWResolution[0], inheader.HWResolution[1]);
    102     fprintf(stderr, "DEBUG: ImagingBoundingBox = [ %d %d %d %d ]\n", inheader.ImagingBoundingBox[0], inheader.ImagingBoundingBox[1], inheader.ImagingBoundingBox[2], inheader.ImagingBoundingBox[3]);
    103     fprintf(stderr, "DEBUG: Margins = [ %d %d ]\n", inheader.Margins[0], inheader.Margins[1]);
    104     fprintf(stderr, "DEBUG: ManualFeed = %d\n", inheader.ManualFeed);
    105     fprintf(stderr, "DEBUG: MediaPosition = %d\n", inheader.MediaPosition);
    106     fprintf(stderr, "DEBUG: NumCopies = %d\n", inheader.NumCopies);
    107     fprintf(stderr, "DEBUG: Orientation = %d\n", inheader.Orientation);
    108     fprintf(stderr, "DEBUG: PageSize = [ %d %d ]\n", inheader.PageSize[0], inheader.PageSize[1]);
    109     fprintf(stderr, "DEBUG: cupsWidth = %d\n", inheader.cupsWidth);
    110     fprintf(stderr, "DEBUG: cupsHeight = %d\n", inheader.cupsHeight);
    111     fprintf(stderr, "DEBUG: cupsMediaType = %d\n", inheader.cupsMediaType);
    112     fprintf(stderr, "DEBUG: cupsBitsPerColor = %d\n", inheader.cupsBitsPerColor);
    113     fprintf(stderr, "DEBUG: cupsBitsPerPixel = %d\n", inheader.cupsBitsPerPixel);
    114     fprintf(stderr, "DEBUG: cupsBytesPerLine = %d\n", inheader.cupsBytesPerLine);
    115     fprintf(stderr, "DEBUG: cupsColorOrder = %d\n", inheader.cupsColorOrder);
    116     fprintf(stderr, "DEBUG: cupsColorSpace = %d\n", inheader.cupsColorSpace);
    117     fprintf(stderr, "DEBUG: cupsCompression = %d\n", inheader.cupsCompression);
    118 
    119    /*
    120     * Compute the real raster size...
    121     */
    122 
    123     page ++;
    124 
    125     fprintf(stderr, "PAGE: %d %d\n", page, inheader.NumCopies);
    126 
    127     page_width  = (unsigned)(inheader.cupsPageSize[0] * inheader.HWResolution[0] / 72.0);
    128     page_height = (unsigned)(inheader.cupsPageSize[1] * inheader.HWResolution[1] / 72.0);
    129     page_left   = (unsigned)(inheader.cupsImagingBBox[0] * inheader.HWResolution[0] / 72.0);
    130     page_bottom = (unsigned)(inheader.cupsImagingBBox[1] * inheader.HWResolution[1] / 72.0);
    131     page_top    = page_height - page_bottom - inheader.cupsHeight;
    132     linesize    = (page_width * inheader.cupsBitsPerPixel + 7) / 8;
    133     lineoffset  = page_left * inheader.cupsBitsPerPixel / 8; /* Round down */
    134 
    135     if (page_left > page_width || page_top > page_height || page_bottom > page_height)
    136     {
    137       _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
    138       fprintf(stderr, "DEBUG: Bad bottom/left/top margin on page %d.\n", page);
    139       return (1);
    140     }
    141 
    142     switch (inheader.cupsColorSpace)
    143     {
    144       case CUPS_CSPACE_W :
    145       case CUPS_CSPACE_RGB :
    146       case CUPS_CSPACE_SW :
    147       case CUPS_CSPACE_SRGB :
    148       case CUPS_CSPACE_ADOBERGB :
    149           white = 255;
    150 	  break;
    151 
    152       case CUPS_CSPACE_K :
    153       case CUPS_CSPACE_CMYK :
    154       case CUPS_CSPACE_DEVICE1 :
    155       case CUPS_CSPACE_DEVICE2 :
    156       case CUPS_CSPACE_DEVICE3 :
    157       case CUPS_CSPACE_DEVICE4 :
    158       case CUPS_CSPACE_DEVICE5 :
    159       case CUPS_CSPACE_DEVICE6 :
    160       case CUPS_CSPACE_DEVICE7 :
    161       case CUPS_CSPACE_DEVICE8 :
    162       case CUPS_CSPACE_DEVICE9 :
    163       case CUPS_CSPACE_DEVICEA :
    164       case CUPS_CSPACE_DEVICEB :
    165       case CUPS_CSPACE_DEVICEC :
    166       case CUPS_CSPACE_DEVICED :
    167       case CUPS_CSPACE_DEVICEE :
    168       case CUPS_CSPACE_DEVICEF :
    169           white = 0;
    170 	  break;
    171 
    172       default :
    173 	  _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
    174 	  fprintf(stderr, "DEBUG: Unsupported cupsColorSpace %d on page %d.\n",
    175 	          inheader.cupsColorSpace, page);
    176 	  return (1);
    177     }
    178 
    179     if (inheader.cupsColorOrder != CUPS_ORDER_CHUNKED)
    180     {
    181       _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
    182       fprintf(stderr, "DEBUG: Unsupported cupsColorOrder %d on page %d.\n",
    183               inheader.cupsColorOrder, page);
    184       return (1);
    185     }
    186 
    187     if (inheader.cupsBitsPerPixel != 1 &&
    188         inheader.cupsBitsPerColor != 8 && inheader.cupsBitsPerColor != 16)
    189     {
    190       _cupsLangPrintFilter(stderr, "ERROR", _("Unsupported raster data."));
    191       fprintf(stderr, "DEBUG: Unsupported cupsBitsPerColor %d on page %d.\n",
    192               inheader.cupsBitsPerColor, page);
    193       return (1);
    194     }
    195 
    196     memcpy(&outheader, &inheader, sizeof(outheader));
    197     outheader.cupsWidth        = page_width;
    198     outheader.cupsHeight       = page_height;
    199     outheader.cupsBytesPerLine = linesize;
    200 
    201     outheader.cupsInteger[14]  = 0;	/* VendorIdentifier */
    202     outheader.cupsInteger[15]  = 0;	/* VendorLength */
    203 
    204     if ((val = cupsGetOption("print-content-optimize", num_options,
    205                              options)) != NULL)
    206     {
    207       if (!strcmp(val, "automatic"))
    208         strlcpy(outheader.OutputType, "Automatic",
    209                 sizeof(outheader.OutputType));
    210       else if (!strcmp(val, "graphics"))
    211         strlcpy(outheader.OutputType, "Graphics", sizeof(outheader.OutputType));
    212       else if (!strcmp(val, "photo"))
    213         strlcpy(outheader.OutputType, "Photo", sizeof(outheader.OutputType));
    214       else if (!strcmp(val, "text"))
    215         strlcpy(outheader.OutputType, "Text", sizeof(outheader.OutputType));
    216       else if (!strcmp(val, "text-and-graphics"))
    217         strlcpy(outheader.OutputType, "TextAndGraphics",
    218                 sizeof(outheader.OutputType));
    219       else
    220       {
    221         fputs("DEBUG: Unsupported print-content-optimize value.\n", stderr);
    222         outheader.OutputType[0] = '\0';
    223       }
    224     }
    225 
    226     if ((val = cupsGetOption("print-quality", num_options, options)) != NULL)
    227     {
    228       unsigned quality = (unsigned)atoi(val);		/* print-quality value */
    229 
    230       if (quality >= IPP_QUALITY_DRAFT && quality <= IPP_QUALITY_HIGH)
    231 	outheader.cupsInteger[8] = quality;
    232       else
    233       {
    234 	fprintf(stderr, "DEBUG: Unsupported print-quality %d.\n", quality);
    235 	outheader.cupsInteger[8] = 0;
    236       }
    237     }
    238 
    239     if ((val = cupsGetOption("print-rendering-intent", num_options,
    240                              options)) != NULL)
    241     {
    242       if (!strcmp(val, "absolute"))
    243         strlcpy(outheader.cupsRenderingIntent, "Absolute",
    244                 sizeof(outheader.cupsRenderingIntent));
    245       else if (!strcmp(val, "automatic"))
    246         strlcpy(outheader.cupsRenderingIntent, "Automatic",
    247                 sizeof(outheader.cupsRenderingIntent));
    248       else if (!strcmp(val, "perceptual"))
    249         strlcpy(outheader.cupsRenderingIntent, "Perceptual",
    250                 sizeof(outheader.cupsRenderingIntent));
    251       else if (!strcmp(val, "relative"))
    252         strlcpy(outheader.cupsRenderingIntent, "Relative",
    253                 sizeof(outheader.cupsRenderingIntent));
    254       else if (!strcmp(val, "relative-bpc"))
    255         strlcpy(outheader.cupsRenderingIntent, "RelativeBpc",
    256                 sizeof(outheader.cupsRenderingIntent));
    257       else if (!strcmp(val, "saturation"))
    258         strlcpy(outheader.cupsRenderingIntent, "Saturation",
    259                 sizeof(outheader.cupsRenderingIntent));
    260       else
    261       {
    262         fputs("DEBUG: Unsupported print-rendering-intent value.\n", stderr);
    263         outheader.cupsRenderingIntent[0] = '\0';
    264       }
    265     }
    266 
    267     if (inheader.cupsPageSizeName[0] &&
    268         (pwg_size = _ppdCacheGetSize(cache, inheader.cupsPageSizeName)) != NULL)
    269     {
    270       strlcpy(outheader.cupsPageSizeName, pwg_size->map.pwg,
    271 	      sizeof(outheader.cupsPageSizeName));
    272     }
    273     else
    274     {
    275       pwg_media = pwgMediaForSize((int)(2540.0 * inheader.cupsPageSize[0] / 72.0),
    276 				  (int)(2540.0 * inheader.cupsPageSize[1] / 72.0));
    277 
    278       if (pwg_media)
    279         strlcpy(outheader.cupsPageSizeName, pwg_media->pwg,
    280                 sizeof(outheader.cupsPageSizeName));
    281       else
    282       {
    283         fprintf(stderr, "DEBUG: Unsupported PageSize %.2fx%.2f.\n",
    284                 inheader.cupsPageSize[0], inheader.cupsPageSize[1]);
    285         outheader.cupsPageSizeName[0] = '\0';
    286       }
    287     }
    288 
    289     if (inheader.Duplex && !(page & 1) &&
    290         back && _cups_strcasecmp(back->value, "Normal"))
    291     {
    292       if (_cups_strcasecmp(back->value, "Flipped"))
    293       {
    294         if (inheader.Tumble)
    295         {
    296 	  outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */
    297 	  outheader.cupsInteger[2] = 1;	/* FeedTransform */
    298 
    299 	  outheader.cupsInteger[3] = page_width - page_left -
    300 	                             inheader.cupsWidth;
    301 					/* ImageBoxLeft */
    302 	  outheader.cupsInteger[4] = page_top;
    303 					/* ImageBoxTop */
    304 	  outheader.cupsInteger[5] = page_width - page_left;
    305       					/* ImageBoxRight */
    306 	  outheader.cupsInteger[6] = page_height - page_bottom;
    307       					/* ImageBoxBottom */
    308         }
    309         else
    310         {
    311 	  outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
    312 	  outheader.cupsInteger[2] = ~0U;/* FeedTransform */
    313 
    314 	  outheader.cupsInteger[3] = page_left;
    315 					/* ImageBoxLeft */
    316 	  outheader.cupsInteger[4] = page_bottom;
    317 					/* ImageBoxTop */
    318 	  outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
    319       					/* ImageBoxRight */
    320 	  outheader.cupsInteger[6] = page_height - page_top;
    321       					/* ImageBoxBottom */
    322         }
    323       }
    324       else if (_cups_strcasecmp(back->value, "ManualTumble"))
    325       {
    326         if (inheader.Tumble)
    327         {
    328 	  outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */
    329 	  outheader.cupsInteger[2] = ~0U;/* FeedTransform */
    330 
    331 	  outheader.cupsInteger[3] = page_width - page_left -
    332 	                             inheader.cupsWidth;
    333 					/* ImageBoxLeft */
    334 	  outheader.cupsInteger[4] = page_bottom;
    335 					/* ImageBoxTop */
    336 	  outheader.cupsInteger[5] = page_width - page_left;
    337       					/* ImageBoxRight */
    338 	  outheader.cupsInteger[6] = page_height - page_top;
    339       					/* ImageBoxBottom */
    340         }
    341         else
    342         {
    343 	  outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
    344 	  outheader.cupsInteger[2] = 1;	/* FeedTransform */
    345 
    346 	  outheader.cupsInteger[3] = page_left;
    347 					/* ImageBoxLeft */
    348 	  outheader.cupsInteger[4] = page_top;
    349 					/* ImageBoxTop */
    350 	  outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
    351       					/* ImageBoxRight */
    352 	  outheader.cupsInteger[6] = page_height - page_bottom;
    353       					/* ImageBoxBottom */
    354         }
    355       }
    356       else if (_cups_strcasecmp(back->value, "Rotated"))
    357       {
    358         if (inheader.Tumble)
    359         {
    360 	  outheader.cupsInteger[1] = ~0U;/* CrossFeedTransform */
    361 	  outheader.cupsInteger[2] = ~0U;/* FeedTransform */
    362 
    363 	  outheader.cupsInteger[3] = page_width - page_left -
    364 	                             inheader.cupsWidth;
    365 					/* ImageBoxLeft */
    366 	  outheader.cupsInteger[4] = page_bottom;
    367 					/* ImageBoxTop */
    368 	  outheader.cupsInteger[5] = page_width - page_left;
    369       					/* ImageBoxRight */
    370 	  outheader.cupsInteger[6] = page_height - page_top;
    371       					/* ImageBoxBottom */
    372         }
    373         else
    374         {
    375 	  outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
    376 	  outheader.cupsInteger[2] = 1;	/* FeedTransform */
    377 
    378 	  outheader.cupsInteger[3] = page_left;
    379 					/* ImageBoxLeft */
    380 	  outheader.cupsInteger[4] = page_top;
    381 					/* ImageBoxTop */
    382 	  outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
    383       					/* ImageBoxRight */
    384 	  outheader.cupsInteger[6] = page_height - page_bottom;
    385       					/* ImageBoxBottom */
    386         }
    387       }
    388       else
    389       {
    390        /*
    391         * Unsupported value...
    392         */
    393 
    394         fputs("DEBUG: Unsupported cupsBackSide value.\n", stderr);
    395 
    396 	outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
    397 	outheader.cupsInteger[2] = 1;	/* FeedTransform */
    398 
    399 	outheader.cupsInteger[3] = page_left;
    400 					/* ImageBoxLeft */
    401 	outheader.cupsInteger[4] = page_top;
    402 					/* ImageBoxTop */
    403 	outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
    404       					/* ImageBoxRight */
    405 	outheader.cupsInteger[6] = page_height - page_bottom;
    406       					/* ImageBoxBottom */
    407       }
    408     }
    409     else
    410     {
    411       outheader.cupsInteger[1] = 1;	/* CrossFeedTransform */
    412       outheader.cupsInteger[2] = 1;	/* FeedTransform */
    413 
    414       outheader.cupsInteger[3] = page_left;
    415 					/* ImageBoxLeft */
    416       outheader.cupsInteger[4] = page_top;
    417 					/* ImageBoxTop */
    418       outheader.cupsInteger[5] = page_left + inheader.cupsWidth;
    419       					/* ImageBoxRight */
    420       outheader.cupsInteger[6] = page_height - page_bottom;
    421       					/* ImageBoxBottom */
    422     }
    423 
    424     if (!cupsRasterWriteHeader2(outras, &outheader))
    425     {
    426       _cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
    427       fprintf(stderr, "DEBUG: Unable to write header for page %d.\n", page);
    428       return (1);
    429     }
    430 
    431    /*
    432     * Copy raster data...
    433     */
    434 
    435     if (linesize < inheader.cupsBytesPerLine)
    436       linesize = inheader.cupsBytesPerLine;
    437 
    438     if ((lineoffset + inheader.cupsBytesPerLine) > linesize)
    439       lineoffset = linesize - inheader.cupsBytesPerLine;
    440 
    441     line = malloc(linesize);
    442 
    443     memset(line, white, linesize);
    444     for (y = page_top; y > 0; y --)
    445       if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
    446       {
    447 	_cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
    448 	fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
    449 	        page_top - y + 1, page);
    450 	return (1);
    451       }
    452 
    453     for (y = inheader.cupsHeight; y > 0; y --)
    454     {
    455       if (cupsRasterReadPixels(inras, line + lineoffset, inheader.cupsBytesPerLine) != inheader.cupsBytesPerLine)
    456       {
    457 	_cupsLangPrintFilter(stderr, "ERROR", _("Error reading raster data."));
    458 	fprintf(stderr, "DEBUG: Unable to read line %d for page %d.\n",
    459 	        inheader.cupsHeight - y + page_top + 1, page);
    460 	return (1);
    461       }
    462 
    463       if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
    464       {
    465 	_cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
    466 	fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
    467 	        inheader.cupsHeight - y + page_top + 1, page);
    468 	return (1);
    469       }
    470     }
    471 
    472     memset(line, white, linesize);
    473     for (y = page_bottom; y > 0; y --)
    474       if (!cupsRasterWritePixels(outras, line, outheader.cupsBytesPerLine))
    475       {
    476 	_cupsLangPrintFilter(stderr, "ERROR", _("Error sending raster data."));
    477 	fprintf(stderr, "DEBUG: Unable to write line %d for page %d.\n",
    478 	        page_bottom - y + page_top + inheader.cupsHeight + 1, page);
    479 	return (1);
    480       }
    481 
    482     free(line);
    483   }
    484 
    485   cupsRasterClose(inras);
    486   if (fd)
    487     close(fd);
    488 
    489   cupsRasterClose(outras);
    490 
    491   return (0);
    492 }
    493