#include <stdlib.h>
#include <string.h>
#include <signal.h>
+#include <ctype.h>
+#include <limits.h>
#include <cups/cups.h>
+#include <ppd/ppd.h>
int /* O - Error status */
universal(int inputfd, /* I - File descriptor input stream */
(input/output format) */
{
char *input;
- char *output;
- char *input_super = malloc(16);
- char *input_type = malloc(256);
- char *output_super = malloc(16);
- char *output_type = malloc(256);
+ char *final_output;
+ char output[256];
+ char input_super[16];
+ char input_type[256];
+ char output_super[16];
+ char output_type[256];
filter_out_format_t *outformat;
filter_filter_in_chain_t *filter, *next;
filter_input_output_format_t input_output_format;
+ ppd_file_t *ppd;
+ ppd_cache_t *cache;
filter_logfunc_t log = data->logfunc;
void *ld = data->logdata;
int ret = 0;
input_output_format = *(filter_input_output_format_t *)parameters;
input = input_output_format.input_format;
- output = input_output_format.output_format;
+ final_output = input_output_format.output_format;
+ strncpy(output, final_output, sizeof(output) - 1);
+
+ /* If we have a PPD, load it and its cache, so that we can access the
+ "*cupsFilter(2): ..." lines */
+ if (data->ppd == NULL && data->ppdfile)
+ data->ppd = ppdOpenFile(data->ppdfile);
+ ppd = data->ppd;
+ if (ppd && !ppd->cache)
+ ppd->cache = ppdCacheCreateWithPPD(ppd);
+ cache = ppd ? ppd->cache : NULL;
+
+ /* Check whether our output format (under CUPS it is taken from the
+ FINAL_CONTENT_TYPE env variable) is the destination format (2nd
+ word) of a "*cupsFilter2: ..." line (string has 4 words), in this
+ case the specified filter (4th word) does the last step,
+ converting from the input format (1st word) of the line to the
+ destination format and so we only need to convert to the input
+ format. In this case we need to correct our output format.
+
+ If there is more than one line with the given output format and
+ an inpout format we can produce, we select the one with the
+ lowest cost value (3rd word) as this is the one which CUPS should have
+ chosen for this job.
+
+ If we have "*cupsFilter: ..." lines (without "2", string with 3
+ words) we do not need to do anything special, as the input format
+ specified is the FIMAL_CONTENT_TYPE which CUPS supplies to us and
+ into which we have to convert. So we quit parsing if the first
+ line has only 3 words, as if CUPS uses the "*cupsFilter: ..."
+ lines only if there is no "*cupsFilter2: ..." line min the PPD,
+ so if we encounter a line with only 3 words the other lines will
+ have only 3 words, too and nothing has to be done. */
+
+ if (ppd && ppd->num_filters && cache)
+ {
+ int lowest_cost = INT_MAX;
+ char *filter;
+
+ if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+ "universal: \"*cupsFilter(2): ...\" lines in the PPD file:");
+
+ for (filter = (char *)cupsArrayFirst(cache->filters);
+ filter;
+ filter = (char *)cupsArrayNext(cache->filters))
+ {
+ char buf[256];
+ char *ptr,
+ *in = NULL,
+ *out = NULL,
+ *coststr = NULL;
+ int cost;
+
+ if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+ "universal: %s", filter);
+
+ /* String of the "*cupsfilter:" or "*cupsfilter2:" line */
+ strncpy(buf, filter, sizeof(buf) - 1);
+
+ /* Separate the words */
+ in = ptr = buf;
+ while (*ptr && !isspace(*ptr)) ptr ++;
+ if (!*ptr) goto error;
+ *ptr = '\0';
+ ptr ++;
+ while (*ptr && isspace(*ptr)) ptr ++;
+ if (!*ptr) goto error;
+ out = ptr;
+ while (*ptr && !isspace(*ptr)) ptr ++;
+ if (!*ptr) goto error;
+ *ptr = '\0';
+ ptr ++;
+ while (*ptr && isspace(*ptr)) ptr ++;
+ if (!*ptr) goto error;
+ coststr = ptr;
+ if (!isdigit(*ptr)) goto error;
+ while (*ptr && !isspace(*ptr)) ptr ++;
+ if (!*ptr) goto error;
+ *ptr = '\0';
+ ptr ++;
+ while (*ptr && isspace(*ptr)) ptr ++;
+ if (!*ptr) goto error;
+ cost = atoi(coststr);
+
+ /* Valid "*cupsFilter2: ..." line ... */
+ if (/* Must be of lower cost than what we selected before */
+ cost < lowest_cost &&
+ /* Must have our FINAL_CONTENT_TYPE as output */
+ strcasecmp(out, final_output) == 0 &&
+ /* Must have as input a format we are able to produce */
+ (strcasecmp(in, "application/vnd.cups-raster") == 0 ||
+ strcasecmp(in, "application/vnd.cups-pdf") == 0 ||
+ strcasecmp(in, "application/vnd.cups-postscript") == 0 ||
+ strcasecmp(in, "application/pdf") == 0 ||
+ strcasecmp(in, "image/pwg-raster") == 0 ||
+ strcasecmp(in, "image/urf") == 0 ||
+ strcasecmp(in, "application/PCLm") == 0 ||
+ strcasecmp(in, "application/postscript") == 0))
+ {
+ if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+ "universal: --> Selecting this line");
+ /* Take the input format of the line as output format for us */
+ strncpy(output, in, sizeof(output));
+ /* Update the minimum cost found */
+ lowest_cost = cost;
+ /* We cannot find a "better" solution ... */
+ if (lowest_cost == 0)
+ {
+ if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+ "universal: Cost value is down to zero, stopping reading further lines");
+ break;
+ }
+ }
+
+ continue;
+
+ error:
+ if (lowest_cost == INT_MAX && coststr)
+ {
+ if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+ "universal: PPD uses \"*cupsFilter: ...\" lines, so we always convert to format given by FINAL_CONTENT_TYPE");
+ break;
+ }
+ if (log) log(ld, FILTER_LOGLEVEL_ERROR,
+ "universal: Invalid \"*cupsFilter2: ...\" line in PPD: %s",
+ filter);
+ }
+
+ }
+
+ if (strcasecmp(output, final_output) != 0)
+ {
+ if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+ "universal: Converting from %s to %s, final output will be %s",
+ input, output, final_output);
+ }
+ else
+ {
+ if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
+ "universal: Converting from %s to %s", input, output);
+ }
+
sscanf(input, "%15[^/]/%255s", input_super, input_type);
sscanf(output, "%15[^/]/%255s", output_super, output_type);
!strcmp(output_type, "pwg-raster") ||
!strcmp(output_type, "PCLm"))
{
+ outformat = malloc(sizeof(filter_out_format_t));
+ *outformat = OUTPUT_FORMAT_CUPS_RASTER;
+ if (!strcmp(output_type, "pwg-raster") ||
+ (!strcmp(output_type, "vnd.cups-raster") &&
+ !strcmp(final_output, "image/pwg-raster")))
+ *outformat = OUTPUT_FORMAT_PWG_RASTER;
+ else if (!strcmp(output_type, "urf") ||
+ (!strcmp(output_type, "vnd.cups-raster") &&
+ !strcmp(final_output, "image/urf")))
+ *outformat = OUTPUT_FORMAT_APPLE_RASTER;
+ else if (!strcmp(output_type, "PCLm") ||
+ (!strcmp(output_type, "vnd.cups-raster") &&
+ !strcmp(final_output, "applicationn/PCLm")))
+ *outformat = OUTPUT_FORMAT_PCLM;
filter = malloc(sizeof(filter_filter_in_chain_t));
filter->function = imagetoraster;
- filter->parameters = NULL;
+ filter->parameters = outformat;
filter->name = "imagetoraster";
cupsArrayAdd(filter_chain, filter);
if (log) log(ld, FILTER_LOGLEVEL_DEBUG,
*outformat = OUTPUT_FORMAT_CUPS_RASTER;
if (!strcmp(output_type, "pwg-raster"))
*outformat = OUTPUT_FORMAT_PWG_RASTER;
- else if(!strcmp(output_type, "urf"))
+ else if(!strcmp(output_type, "urf") ||
+ (!strcmp(output_type, "vnd.cups-raster") &&
+ !strcmp(final_output, "image/urf")))
*outformat = OUTPUT_FORMAT_APPLE_RASTER;
else if(!strcmp(output_type, "PCLm"))
*outformat = OUTPUT_FORMAT_PCLM;
*outformat = OUTPUT_FORMAT_CUPS_RASTER;
if (!strcmp(output_type, "pwg-raster"))
*outformat = OUTPUT_FORMAT_PWG_RASTER;
- else if(!strcmp(output_type, "urf"))
+ else if (!strcmp(output_type, "urf") ||
+ (!strcmp(output_type, "vnd.cups-raster") &&
+ !strcmp(final_output, "image/urf")))
*outformat = OUTPUT_FORMAT_APPLE_RASTER;
else if(!strcmp(output_type, "PCLm"))
*outformat = OUTPUT_FORMAT_PCLM;
input, output);
}
else
+ /* Do the dirty work ... */
ret = filterChain(inputfd, outputfd, inputseekable, data, filter_chain);
- free(input_super);
- free(input_type);
- free(output_super);
- free(output_type);
for (filter = (filter_filter_in_chain_t *)cupsArrayFirst(filter_chain);
filter; filter = next)
{