/**
* Copyright (c) 2005 Hin-Tak Leung. All rights reserved.
*
**/
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
**/
/**
* Copyright (c) 2001-2002 artofcode LLC.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ijs.h"
#include "ijs_server.h"
#include "i_job_info.h"
#define BUF_SIZE 4096
typedef struct _I_ParamList ParamList;
struct _I_ParamList {
ParamList *next;
char *key;
char *value;
int value_size;
};
static int
i_status_cb (void *status_cb_data,
IjsServerCtx *ctx,
IjsJobId job_id)
{
return 0;
}
static int
i_list_cb (void *list_cb_data,
IjsServerCtx *ctx,
IjsJobId job_id,
char *val_buf,
int val_size)
{
const char *param_list = "OutputFile,OutputFD,DeviceManufacturer,DeviceModel,PageImageFormat,Dpi,Width,Height,BitsPerSample,ByteSex,ColorSpace,NumChan,PaperSize,PrintableArea,PrintableTopLeft,TopLeft,PS:Duplex,PS:Tumble,Quality,Quality:Quality,PaperType,Copies";
int size = strlen (param_list);
if (size > val_size)
{
return IJS_EBUF;
}
memcpy (val_buf, param_list, size);
return size;
}
static int
i_enum_cb (void *enum_cb_data,
IjsServerCtx *ctx,
IjsJobId job_id,
const char *key,
char *val_buf,
int val_size)
{
const char *val = NULL;
if (!strcmp (key, "ColorSpace"))
val = "DeviceRGB,DeviceGray,DeviceCMYK";
else if (!strcmp (key, "DeviceManufacturer"))
val = "HEWLETT-PACKARD";
else if (!strcmp (key, "DeviceModel"))
val = "hp color LaserJet 3500,hp color LaserJet 3550";
else if (!strcmp (key, "PageImageFormat"))
val = "Raster";
else if (!strcmp (key, "PS:Duplex"))
val = "true,false";
else if (!strcmp (key, "PS:Tumble"))
val = "true,false";
if (val == NULL)
return IJS_EUNKPARAM;
else
{
int size = strlen (val);
if (size > val_size)
return IJS_EBUF;
memcpy (val_buf, val, size);
return size;
}
}
/* A C implementation of /^(\d\.+\-eE)+x(\d\.+\-eE)+$/ */
static int
i_parse_wxh (const char *val, int size,
double *pw, double *ph)
{
char buf[256];
char *tail;
int i;
for (i = 0; i < size; i++)
if (val[i] == 'x')
break;
if (i + 1 >= size)
return IJS_ESYNTAX;
if (i >= sizeof(buf))
return IJS_EBUF;
memcpy (buf, val, i);
buf[i] = 0;
*pw = strtod (buf, &tail);
if (tail == buf)
return IJS_ESYNTAX;
if (size - i > sizeof(buf))
return IJS_EBUF;
memcpy (buf, val + i + 1, size - i - 1);
buf[size - i - 1] = 0;
*ph = strtod (buf, &tail);
if (tail == buf)
return IJS_ESYNTAX;
return 0;
}
/**
* i_find_key: Search parameter list for key.
*
* @key: key to look up
*
* Return value: ParamList entry matching @key, or NULL.
**/
static ParamList *
i_find_key (ParamList *pl, const char *key)
{
ParamList *curs;
for (curs = pl; curs != NULL; curs = curs->next)
{
if (!strcmp (curs->key, key))
return curs;
}
return NULL;
}
static int
set_param (ParamList **ppl, const char *key, const char *value, int value_size)
{
int key_len = strlen (key);
ParamList *pl;
#ifdef DEBUG
fprintf(stderr," set_param: %s=",key);
fwrite (value, 1, value_size, stderr);
fputs ("\n", stderr);
#endif
pl = i_find_key (*ppl, key);
if (pl == NULL)
{
pl = (ParamList *)malloc (sizeof (ParamList));
pl->next = *ppl;
pl->key = malloc (key_len + 1);
memcpy (pl->key, key, key_len + 1);
*ppl = pl;
}
else
{
free (pl->value);
}
pl->value = malloc (value_size);
memcpy (pl->value, value, value_size);
pl->value_size = value_size;
return 0;
}
/**
* @printable: An array in which to store the printable area.
*
* On return, @printable = PrintableArea[0:1] + TopLeft[0:1]
**/
static int
i_compute_printable (ParamList *pl, double printable[4])
{
ParamList *curs;
double width, height;
int code;
double margin = 0.1667; /* The unprintable margin */
curs = i_find_key (pl, "PaperSize");
if (curs == NULL)
return -1;
code = i_parse_wxh (curs->value, curs->value_size, &width, &height);
if (code == 0)
{
/* We impose symmetric margins */
printable[0] = width - 2 * margin;
printable[1] = height - 2 * margin;
printable[2] = margin;
printable[3] = margin;
}
return code;
}
static int
i_get_cb (void *get_cb_data,
IjsServerCtx *ctx,
IjsJobId job_id,
const char *key,
char *val_buf,
int val_size)
{
ParamList *pl = *(ParamList **)get_cb_data;
ParamList *curs;
const char *val = NULL;
char buf[256];
int code;
curs = i_find_key (pl, key);
if (curs != NULL)
{
if (curs->value_size > val_size)
return IJS_EBUF;
memcpy (val_buf, curs->value, curs->value_size);
return curs->value_size;
}
if (!strcmp (key, "PrintableArea") || !strcmp (key, "PrintableTopLeft"))
{
double printable[4];
int off = !strcmp (key, "PrintableArea") ? 0 : 2;
code = i_compute_printable (pl, printable);
if (code == 0)
{
sprintf (buf, "%gx%g", printable[off + 0], printable[off + 1]);
val = buf;
}
}
if (!strcmp (key, "DeviceManufacturer"))
val = "HEWLETT-PACKARD";
else if (!strcmp (key, "DeviceModel"))
val = "hp color LaserJet 3550";
else if (!strcmp (key, "PageImageFormat"))
val = "Raster";
else if (!strcmp (key, "Dpi"))
val = "600x600";
else if (!strcmp (key, "PS:Duplex"))
val = "false";
else if (!strcmp (key, "PS:Tumble"))
val = "false";
if (val == NULL)
return IJS_EUNKPARAM;
else
{
int size = strlen (val);
if (size > val_size)
return IJS_EBUF;
memcpy (val_buf, val, size);
return size;
}
}
static int
i_set_cb (void *set_cb_data, IjsServerCtx *ctx, IjsJobId job_id,
const char *key, const char *value, int value_size)
{
ParamList **ppl = (ParamList **)set_cb_data;
int code;
char buf[256];
/* Convert value to a zero-terminated string */
if( value_size >= sizeof(buf)){
return IJS_EBUF;
}
memcpy (buf, value, value_size);
buf[value_size] = 0;
if (!strcmp (key, "PaperSize"))
{
/* Setting PaperSize affects PrintableArea, which is used by ghostscript */
double width, height;
code = i_parse_wxh (value, value_size, &width, &height);
if (code < 0)
return code;
}
if (!strcmp (key, "Dpi"))
{
char *s = NULL;
if(!strcmp (buf, "600"))
{
s = "600x600";
}
else
{
return IJS_ERANGE;
}
code = set_param (ppl, "Dpi", s, strlen (s));
if (code < 0)
return code; /* because we can't go on setting Dpi */
}
if (!strcmp (key, "PrintableArea"))
{
/* This can't be set */
fprintf (stderr, "Setting %s is not allowed\n", key);
return IJS_ERANGE;
}
if (!strcmp (key, "PrintableTopLeft"))
{
/* This can't be set */
fprintf (stderr, "Setting %s is not allowed\n", key);
return IJS_ERANGE;
}
/* Update parameter list */
code = set_param (ppl, key, value, value_size);
if (code < 0)
return code;
return 0;
}
/**
* Finds a parameter in the param list, and allocates a null terminated
* string with the value.
**/
static char *
find_param (ParamList *pl, const char *key)
{
ParamList *curs;
char *result;
curs = i_find_key (pl, key);
if (curs == NULL)
return NULL;
result = malloc (curs->value_size + 1);
memcpy (result, curs->value, curs->value_size);
result[curs->value_size] = 0;
return result;
}
static void
free_param_list (ParamList *pl)
{
ParamList *next;
for (; pl != NULL; pl = next)
{
next = pl->next;
free (pl->key);
free (pl->value);
free (pl);
}
}
static int
pl_to_jobinfo (ParamList *pl, IjsPageHeader ph, i_job_info_t *job_info)
{
char *fn;
char *s;
/* Check options */
if (job_info->outfile == NULL)
{
fn = find_param (pl, "OutputFile");
/* todo: check error! */
if (fn == NULL)
{
fn = find_param (pl, "OutputFD");
if (fn != NULL)
{
job_info->outfile = fdopen (atoi (fn), "w");
}
}
else
{
job_info->outfile = fopen (fn, "w");
}
if (job_info->outfile == NULL)
{
fclose (stdin);
fclose (stdout);
return 1;
}
if (fn != NULL)
free (fn);
}
/* DeviceModel */
s = find_param(pl, "DeviceModel");
if (s == NULL)
{
fprintf(stderr, "Printer DeviceModel not set, aborting!\n");
return 1;
}
if (strcmp(s, "hp color LaserJet 3550") == 0)
{
job_info->model = MODEL_CLJ3550 ;
}
else if (strcmp(s, "hp color LaserJet 3500") == 0)
{
job_info->model = MODEL_CLJ3500 ;
}
else
{
fprintf(stderr, "Unknown Printer %s, aborting!\n", s);
return 1;
}
/* PaperSize */ /* FIXME */
s = find_param(pl, "PaperSize");
if (s == NULL)
{
fprintf(stderr, "PaperSize not set, using default (A4 210x297mm)\n");
}
else
{
double width, height;
int code;
code = i_parse_wxh (s, strlen(s), &width, &height);
if (code == 0)
{
fprintf(stderr, "PaperSize = %s\n", s);
}
else
{
fprintf(stderr, "Unparsable PaperSize %s, aborting!\n", s);
return 1;
}
}
/* Dpi */
s = find_param(pl, "Dpi");
if (s == NULL)
{
fprintf(stderr, "Dpi not set, using default (600)\n");
job_info->dpi_h = 600;
job_info->dpi_v = 600;
}
else if (strcmp(s, "600") == 0)
{
job_info->dpi_h = 600;
job_info->dpi_v = 600;
}
else
{
fprintf(stderr, "Unknown Dpi value %s, aborting!\n", s);
return 1;
}
/* PaperType */
s = find_param(pl, "PaperType");
if (s == NULL)
{
fprintf(stderr, "PaperType not set, using default Normal (0)\n");
job_info->papertype = 0;
}
else
{
fprintf(stderr, "Unknown PaperType value %s, aborting!\n", s);
return 1;
}
/* Copies */
s = find_param(pl, "Copies");
if (s == NULL)
{
fprintf(stderr, "Copies not set, using default (1)\n");
job_info->copies = 1;
}
else
{
job_info->copies = atoi(s);
if(!job_info->copies)
{
fprintf(stderr, "Unknown Copies value %s, aborting!\n", s);
return 1;
}
else
{
fprintf(stderr, "Printing %d copies\n", job_info->copies);
}
}
/* Duplex */
s = find_param(pl, "PS:Duplex");
if (s == NULL)
{
fprintf(stderr, "Duplex not set, using default Off (0)\n");
job_info->duplex = 0;
}
else
if (strncmp(s, "true", 4) == 0)
job_info->duplex = 1;
else if (strncmp(s, "false", 5) == 0)
job_info->duplex = 0;
else
{
fprintf(stderr, "Unknown Duplex value %s, aborting!\n", s);
return 1;
}
/* Tumble */
s = find_param(pl, "PS:Tumble");
if (s == NULL)
{
fprintf(stderr, "Tumble not set, using default Off (0)\n");
job_info->tumble = 0;
}
else
if (strncmp(s, "true", 4) == 0)
job_info->tumble = 1;
else if (strncmp(s, "false", 5) == 0)
job_info->tumble = 0;
else
{
fprintf(stderr, "Unknown Tumble value %s, aborting!\n", s);
return 1;
}
/* Quality */
s = find_param(pl, "Quality:Quality");
if (s == NULL)
{
fprintf(stderr, "Quality not set, set HPIJS compatible\n");
job_info->qfactor = 6;
}
else
{
int quality = atoi(s);
switch(quality)
{
case 2:
job_info->qfactor = 0;
fprintf(stderr, "Quality: Best\n");
break;
case 1:
job_info->qfactor = 1;
fprintf(stderr, "Quality: Medium\n");
break;
case 0:
job_info->qfactor = 6;
fprintf(stderr, "Quality: Low\n");
break;
default:
job_info->qfactor = 6;
fprintf(stderr, "Quality: unrecognised, set to Low\n");
break;
}
}
/* Number of channels */
if ((ph.n_chan != 1) && (ph.n_chan != 3))
{
fprintf(stderr, "Number of channels is %i (unsupported), aborting!\n",
ph.n_chan);
return 1;
}
else
{
job_info->components = ph.n_chan;
}
/* Bits per sample */
if ((ph.bps != 1) && (ph.bps != 8))
{
fprintf(stderr, "Bit per sample is %i (unsupported), aborting!\n",
ph.bps);
return 1;
}
/* BitMap Size */
job_info->pixel_h = (ph.width + 0x1f) & ~0x1f;
job_info->pixel_v = (ph.height + 0x7f) & ~0x7f;
fprintf(stderr, " Bitmap size %ix%i (%gx%g in)\n",
job_info->pixel_h,
job_info->pixel_v,
(double)job_info->pixel_h / job_info->dpi_h,
(double)job_info->pixel_v / job_info->dpi_v);
/* Dpi of the bitmap */
if (ph.xres != (double)job_info->dpi_h
|| ph.yres != (double)job_info->dpi_v)
{
fprintf(stderr, "Dpi of bitmap (%gx%g) is different from selected dpi (%gx%g)",
ph.xres,
(double)job_info->dpi_h,
ph.yres,
(double)job_info->dpi_v);
return 1;
}
/* If we get here, all it's OK */
return 0;
}
int
main (int argc, char **argv)
{
IjsServerCtx *ctx;
IjsPageHeader ph;
int status;
ParamList *pl = NULL;
i_job_info_t *job_info = NULL;
int i_job_started = JOB_STARTED_NO;
int current_page_number = 1;
if (argc > 1) /* the IJS plugin is being run stand-alone */
{
/* we'll try to get status if the plugin is being run stand-alone */
i_status(argc, argv);
exit(0);
}
ctx = ijs_server_init ();
if (ctx == NULL)
return (1);
ijs_server_install_status_cb (ctx, i_status_cb, &pl);
ijs_server_install_list_cb (ctx, i_list_cb, &pl);
ijs_server_install_enum_cb (ctx, i_enum_cb, &pl);
ijs_server_install_set_cb (ctx, i_set_cb, &pl);
ijs_server_install_get_cb (ctx, i_get_cb, &pl);
i_init(&job_info);
do
{
int total_bytes, bytes_left;
ParamList *curs;
char *ptr_row_prev;
char *ptr_row_current;
int bytes_per_row;
int bytes_per_row_padded;
int total_stripes;
int i_stripe;
fprintf (stderr, "getting page header\n");
status = ijs_server_get_page_header (ctx, &ph);
if (status)
{
if (status < 0)
{
fprintf(stderr, "ijs_server_get_page_header failed: %d\n",
status);
}
break;
}
fprintf (stderr, "Received header for page %d: width %d x height %d\n",
current_page_number, ph.width, ph.height);
current_page_number++;
/* Before starting, dump IJS parameters */
for (curs = pl; curs != NULL; curs = curs->next)
{
fprintf (stderr, "%% IJS parameter: %s = ", curs->key);
fwrite (curs->value, 1, curs->value_size, stderr);
fputs ("\n", stderr);
}
/* Convert parameters to internal format */
status = pl_to_jobinfo(pl, ph, job_info);
if (status != 0)
{
fprintf (stderr, "parameters conversion failed, aborting\n");
exit (1);
}
/* Job Header */
if (i_job_started == JOB_STARTED_NO) /* only do one job header per job */
{
status = i_job_header(job_info);
if (status != 0)
{
fprintf (stderr, "output error\n");
exit (1);
}
i_job_started = JOB_STARTED_YES ;
}
/* Page header */
status = i_page_header(job_info);
if (status != 0)
{
fprintf (stderr, "output error\n");
exit (1);
}
bytes_per_row = (ph.n_chan * ph.bps * ph.width + 8 - 1) / 8 ;
total_bytes = bytes_per_row * ph.height;
bytes_per_row_padded = ((ph.width + 0x1f) & ~0x1f) * ph.n_chan;
/* last one may have to be padded */
total_stripes = (ph.height + 128 - 1) / 128;
ptr_row_prev = (char *)malloc(bytes_per_row_padded);
ptr_row_current = (char *)malloc(bytes_per_row_padded);
bytes_left = total_bytes;
for (i_stripe = 0 ; i_stripe < total_stripes ; i_stripe++)
{
int i, i_row;
/* clear both rows at stripe start */
for (i = 0 ; i < bytes_per_row_padded ; i++)
{
*(ptr_row_current + i) = (char) 0xff;
*(ptr_row_prev + i) = (char) 0xff;
}
memset(job_info->cached_color, 0xff, 3);
for (i_row = 0 ; i_row < 128 ; i_row++)
{
/* Page body */
char *ptr_temp;
if (bytes_left > 0) /* no padding situation */
{
status = ijs_server_get_data (ctx, ptr_row_current, bytes_per_row);
bytes_left -= bytes_per_row;
if (status)
{
fprintf (stderr, "page aborted!\n");
break;
}
}
else
{
/* padding situation */
memcpy (ptr_row_current, ptr_row_prev, bytes_per_row_padded); /* repeat last line */
}
i_compress_row(job_info,
ptr_row_current,
ptr_row_prev, bytes_per_row_padded);
ptr_temp = ptr_row_current;
ptr_row_current = ptr_row_prev;
ptr_row_prev = ptr_temp;
}
i_print_stripe(job_info, i_stripe);
}
/* Page footer */
status = i_page_footer (job_info);
if (status != 0)
{
fprintf (stderr, "output error\n");
exit (1);
}
}
while (status == 0);
/* Job footer */
if (i_job_started == JOB_STARTED_YES)
{
status = i_job_footer (job_info);
}
if (status > 0) status = 0; /* normal exit */
i_exit(job_info);
ijs_server_done (ctx);
free_param_list (pl);
return status;
}