/* -*-C-*- ******************************************************************************** * * File: x2jet.c * RCS: x2jet.c,v 1.23 89/07/17 12:02:51 lori Exp * Description: xpr support for HP LaserJet and PaintJet printers * Author: Larry Rupp, HP Graphics Technology Division * Created: Fri Jul 15 15:22:26 1988 * Modified: Thu Sep 15 11:59:34 1988 (Larry Rupp) ler@hpfcler * Tue Dec 6 10:04:43 PST 1988 (Marc Ayotte) marca@hp-pcd * Language: C * Package: N/A * Status: Released to MIT * * (c) Copyright 1988, Hewlett-Packard Company. * ******************************************************************************** */ /******************************************************** Copyright (c) 1988 by Hewlett-Packard Company Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that Hewlett-Packard not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. ********************************************************/ /* Copyright (c) 1988 X Consortium 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 X CONSORTIUM 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. Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium. */ #include #include #include #include #include #include "xpr.h" #ifdef NLS16 #ifndef NLS #define NLS #endif #endif #ifndef NLS #define catgets(i, sn,mn,s) (s) #else /* NLS */ #define NL_SETN 2 /* set number */ #include extern nl_catd nlmsg_fd; #endif /* NLS */ #ifndef TRUE # define FALSE 0 # define TRUE 1 #endif /* default printable page area (inches) */ #define STDWIDTH 8.0 #define STDHEIGHT 10.5 /* header & trailer character cell size (centipoints) */ #define CHARWIDTH 720 #define CHARHEIGHT 1200 #define XWDHEADERSIZE (sizeof(XWDFileHeader)) #define XCOLORSIZE (sizeof(XColor)) typedef struct { long width, height; } Area; typedef struct { long x, y; } Location; static Area limit; /* image clip limits (dots) */ static Area page; /* printable page size (centipoints) */ static Location headerloc; /* centipoint location of header string */ static Location trailerloc; /* centipoint location of trailer string */ static Location imageloc; /* centipoint location of image */ static int headerlimit; /* number of chars which will printed for */ static int trailerlimit; /* the image's header/trailer strings */ static XWDFileHeader xwd_header; static XColor *xwd_colors; static char *xwd_image; static unsigned long Z_pixel_mask; static int true_scale; typedef struct { unsigned long Rmask, Gmask, Bmask; int Rshift, Gshift, Bshift; } RGBshiftmask; /* Local prototypes */ static void set_image_limits ( int scale, int density, enum orientation orient, Area print_area); static void set_header_trailer_limits (char *header, char *trailer, long printwidth); static void set_print_locations ( int scale, int density, int top, int left, const char *header, const char *trailer, enum orientation orient, int position_on_page); static int scale_raster ( int density, enum orientation orient, Area print_area); static void scale_and_orient_image ( int *scale, int *density, int width, int height, int left, int top, /* in 300ths of an inch */ const char *header, const char *trailer, enum orientation *orient, int position_on_page, enum device device); static void setup_RGBshiftmask (RGBshiftmask *sm, unsigned long rmask, unsigned long gmask, unsigned long bmask); static void swap_black_and_white (void); static void reset_color_mapping (void); static void prepare_color_mapping ( int invert, int paintjet, unsigned int cutoff, FILE *out); static void select_grey (int level, int *r, int *g, int *b); static int load_printer_color ( long index, int nearmatch, enum device device); static int lookup_color_index (long i); static void select_printer_color ( long index, int *red, int *green, int *blue, long *compositeRGB, enum device device); static int color_already_in_printer (long compositeRGB, long *pindex); static int program_new_printer_color ( int red, int green, int blue, long compositeRGB, long *pindex); static long composite_diff (long x, long y); static long find_nearest_programmed_color (long compositeRGB); static void add_index_to_chain ( long cindex, long pindex); static int load_printer_color_DT ( long index, int nearmatch, enum device device); static int load_line_colors ( long *line, int length, int nearmatch, enum device device); static void download_colors ( long *line, int length, enum device device); static void validate_visual(void); static void read_xwd_data (FILE *in); static void write_image_prefix ( FILE *out, int scale, int density, const char *header, enum device device, int position_on_page, int initial_formfeed, enum orientation orient, float gamma, int render, int slide); static void write_image_suffix ( FILE *out, const char *trailer, int position_on_page, int slide, int render, enum device device); static unsigned long Z_image_pixel (int x, int y); static unsigned long XY_image_pixel (int x, int y); static void direct_by_pixel( FILE *out, long *line, int length, enum device device); static void index_by_pixel( FILE *out, long *line, int length); static void write_raster_line ( FILE *out, int scale, enum device device, long *line, int length); static void write_portrait_Z_image ( FILE *out, int scale, enum device device); static void write_landscape_Z_image ( FILE *out, int scale, enum device device); static void write_portrait_XY_image ( FILE *out, int scale, enum device device); static void write_landscape_XY_image ( FILE *out, int scale, enum device device); static void write_Z_image ( FILE *out, int scale, enum orientation orient, enum device device); static void write_XY_image ( FILE *out, int scale, enum orientation orient, enum device device); static void write_image ( FILE *out, int scale, enum orientation orient, enum device device); static void fatal_err (const char *s, ...) _X_NORETURN _X_ATTRIBUTE_PRINTF(1,2); /* Computes the centipoint width of one printer dot. */ #define dot_centipoints(s,d) ((7200.0 * (s)) / (d)) static void set_image_limits ( int scale, int density, enum orientation orient, Area print_area) { Area print_dots; double dotsize; /* Set dotsize to the centipoint width of one printer dot. */ dotsize = dot_centipoints(scale, density); if (orient == PORTRAIT) { print_dots.width = print_area.width / dotsize; print_dots.height = print_area.height / dotsize; } else { print_dots.height = print_area.width / dotsize; print_dots.width = print_area.height / dotsize; } limit.width = (print_dots.width < xwd_header.pixmap_width) ? print_dots.width : xwd_header.pixmap_width; limit.height = (print_dots.height < xwd_header.pixmap_height) ? print_dots.height : xwd_header.pixmap_height; if ((limit.width != xwd_header.pixmap_width) || (limit.height != xwd_header.pixmap_height)) fprintf(stderr,(catgets(nlmsg_fd,NL_SETN,1, "%s: Warning: %d x %d image clipped to %ld x %ld.\n")), progname, xwd_header.pixmap_width, xwd_header.pixmap_height, limit.width, limit.height); } static void set_header_trailer_limits (char *header, char *trailer, long printwidth) { /* Determine the number of header and trailer characters * that will fit into the available printing area. */ headerlimit = header ? (((strlen(header) * CHARWIDTH) <= printwidth) ? strlen(header) : (printwidth / CHARWIDTH)) : 0; if (header && headerlimit != strlen(header)) { fprintf(stderr,(catgets(nlmsg_fd,NL_SETN,2, "%s: Warning: Header string clipped to %d characters.\n")), progname, headerlimit); header[headerlimit] = '\0'; } trailerlimit = trailer ? (((strlen(trailer) * CHARWIDTH) <= printwidth) ? strlen(trailer) : (printwidth / CHARWIDTH)) : 0; if (trailer && trailerlimit != strlen(trailer)) { fprintf(stderr,(catgets(nlmsg_fd,NL_SETN,3, "%s: Warning: Trailer string clipped to %d characters.\n")), progname, trailerlimit); trailer[headerlimit] = '\0'; } } static void set_print_locations ( int scale, int density, int top, int left, const char *header, const char *trailer, enum orientation orient, int position_on_page) { Area image; double dotsize; /* Set dotsize to the centipoint width of one printer dot. */ dotsize = dot_centipoints(scale, density); /* Compute the centipoint size of the clipped image area. */ if (orient == PORTRAIT) { image.width = limit.width * dotsize; image.height = limit.height * dotsize; } else { image.height = limit.width * dotsize; image.width = limit.height * dotsize; } if (position_on_page) { /* set vertical positions */ imageloc.y = (top >= 0) ? top * 24 + ((header) ? CHARHEIGHT : 0) : ((page.height - ((header) ? CHARHEIGHT : 0) - image.height - ((trailer) ? CHARHEIGHT : 0)) / 2) + ((header) ? CHARHEIGHT : 0); headerloc.y = imageloc.y - CHARHEIGHT / 4; trailerloc.y = imageloc.y + image.height + (3 * CHARHEIGHT) / 4; /* set horizontal positions */ if (left >= 0) headerloc.x = imageloc.x = trailerloc.x = left * 24; else { headerloc.x = (page.width - headerlimit * CHARWIDTH) / 2; imageloc.x = (page.width - image.width) / 2; trailerloc.x = (page.width - trailerlimit * CHARWIDTH) / 2; } } } static int scale_raster ( int density, enum orientation orient, Area print_area) { Area image; int h_scale, v_scale; /* Set the image dimensions to the number of centipoints that would be * required for printing at the selected density. */ if (orient == PORTRAIT) { image.width = xwd_header.pixmap_width * 7200 / density; image.height = xwd_header.pixmap_height * 7200 / density; } else { image.height = xwd_header.pixmap_width * 7200 / density; image.width = xwd_header.pixmap_height * 7200 / density; } /* Calculate the maximum image multiplier along * the horizontal and vertical dimensions. */ h_scale = print_area.width / image.width; v_scale = print_area.height / image.height; /* If the image can be expanded, return the lesser of the horizontal and * vertical multipliers. Otherwise, the image will not completely fit * the available print area, so just return 1 as the expansion factor. */ return (((h_scale > 0) && (v_scale > 0)) ? ((h_scale= 0) ? width * 24 : STDWIDTH * 7200; page.height = (height >= 0) ? height * 24 : STDHEIGHT * 7200; /* Paintjet Xl has a mechanical form feed, not a strip feed. It has * a slop of about 1/4 to 1/2 of an inch at the top and bottom. * deduct it from the page height. */ if (device == PJETXL) page.height = page.height - 7200; /* Determine the area usable for the image. This area will be smaller * than the total printable area if margins or header/trailer strings * have been specified. Margins, like width and height discussed above, * are expressed in 300ths of an inch and must be converted to centipoints. * Header and trailer strings each reduce the available image height * by 1/6 inch, or 1200 centipoints (aka CHARHEIGHT). */ usable.width = page.width - ((left > 0) ? (left * 24) : 0); usable.height = page.height - ((top > 0) ? (top * 24) : 0) - ((header) ? CHARHEIGHT : 0) - ((trailer) ? CHARHEIGHT : 0); /* Quit here if there is no usable image space. */ if ((usable.width <= 0) || (usable.height <= 0)) { fatal_err((catgets(nlmsg_fd,NL_SETN,4, "No space available on page for image."))); } /* Determine image orientation. The orientation will only be changed if * it was not specified by a command line option. Portrait mode will be * used if either the usable printing area or the image area are square. * Portrait mode will also be used if the long dimensions of the usable * printing area and the image area match, otherwise landscape mode is * used. Portrait mode really means "don't rotate" and landscape mode * means "rotate". */ if (*orient == UNSPECIFIED) { if ((usable.width == usable.height) || (xwd_header.pixmap_width == xwd_header.pixmap_height)) *orient = PORTRAIT; else *orient = ((usable.width < usable.height) == (xwd_header.pixmap_width < xwd_header.pixmap_height)) ? PORTRAIT : LANDSCAPE; } /* Set the dots-per-inch print density if it was not specified */ if (*density <= 0) { switch(device) { case LJET: *density = 300; break; case PJET: *density = 90; break; case PJETXL: default: *density = 180; break; } } /* Fit image to available area if scale was not specified */ if (*scale <= 0) *scale = scale_raster(*density, *orient, usable); /* Determine image clipping limits */ set_image_limits(*scale, *density, *orient, usable); /* Determine header/trailer string length clipping */ set_header_trailer_limits((char *)header, (char *)trailer, usable.width); /* Calculate locations for page layout */ set_print_locations(*scale, *density, top, left, header, trailer, *orient, position_on_page); } static unsigned short fullintensity; #define BLACK 1 #define WHITE 0 #define EMPTY -1 #define MAX_PJ_COLOR 16 #define RGBmatch(r,g,b,i) (r == i) && (g == i) && (b == i) /* Colormap array is used to map from the Xcolor array (xwd_colors) index * numbers into a pjcolor index number. This style of mapping is done when * interpreting non-Direct/TrueColor visual types. */ static long *colormap; /* Pjcolor array is used to hold the scaled RGB triple values * programmed into the printer. */ static long pjcolor[MAX_PJ_COLOR]; static int color_warning_given = FALSE; /* Global visual type indicator, used to select color interpretation method. */ static char Direct_or_TrueColor; /* Color index element definition, these are linked into a circular list * for interpretation of DirectColor or TrueColor visual types. */ typedef struct colorindex { long index; long pjcolor_index; struct colorindex *next; } COLORINDEX; /* Global data for color interpretation. This structure serves as a home * for the color index lists (only used when processing DirectColor or * TrueColor visual types). It also holds color processing switches and a * pointer to the output file to reduce parameter passing overhead. */ static struct { int PaintJet; int Invert; unsigned int CutOff; FILE *OutFile; COLORINDEX *indexchain, *freechain; RGBshiftmask sm; } color; static void setup_RGBshiftmask ( RGBshiftmask *sm, unsigned long rmask, unsigned long gmask, unsigned long bmask) { sm->Rmask = rmask; sm->Gmask = gmask; sm->Bmask = bmask; sm->Rshift = 0; sm->Gshift = 0; sm->Bshift = 0; if (!rmask) fatal_err((catgets(nlmsg_fd,NL_SETN,5, "red mask for visual is zero."))); if (!gmask) fatal_err((catgets(nlmsg_fd,NL_SETN,6, "green mask for visual is zero."))); if (!bmask) fatal_err((catgets(nlmsg_fd,NL_SETN,7, "blue mask for visual is zero."))); for (; !(rmask & 1); sm->Rshift++) rmask >>= 1; for (; !(gmask & 1); sm->Gshift++) gmask >>= 1; for (; !(bmask & 1); sm->Bshift++) bmask >>= 1; } static void swap_black_and_white (void) { /* Reverse black and white in the Xcolor structure array. */ XColor *color; int n; for (n=xwd_header.ncolors, color=xwd_colors; n>0; n--, color++) if (RGBmatch((color->red & fullintensity), (color->green & fullintensity), (color->blue & fullintensity), fullintensity)) color->red = color->green = color->blue = 0; else if (RGBmatch(color->red, color->green, color->blue, 0)) color->red = color->green = color->blue = fullintensity; } static void reset_color_mapping (void) { int n; long *cmap; COLORINDEX *splice; for (n=0; nnext; color.indexchain->next = color.freechain; color.freechain = splice; color.indexchain = NULL; } } else if (color.PaintJet) for (n=xwd_header.ncolors, cmap=colormap; n>0; n--, cmap++) *cmap = EMPTY; } #define Intensity(r,g,b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11) static void prepare_color_mapping ( int invert, int paintjet, unsigned int cutoff, FILE *out) { int n; long *cmap; XColor *xcolor; for (n = xwd_header.bits_per_rgb, fullintensity = 0; n > 0; n--) fullintensity = (fullintensity << 1) | 1; for (n = sizeof(short) * 8 - xwd_header.bits_per_rgb; n > 0; n--) fullintensity = (fullintensity << 1); Direct_or_TrueColor = (xwd_header.visual_class == DirectColor || xwd_header.visual_class == TrueColor); color.PaintJet = paintjet; color.Invert = invert; color.CutOff = cutoff; color.OutFile = out; color.indexchain = NULL; color.freechain = NULL; if (Direct_or_TrueColor) setup_RGBshiftmask(&color.sm, xwd_header.red_mask, xwd_header.green_mask, xwd_header.blue_mask); else { if (!(colormap = malloc(xwd_header.ncolors * sizeof(long)))) fatal_err((catgets(nlmsg_fd,NL_SETN,24, "Could not allocate memory for X-to-printer colormap."))); /* For PaintJet, color map assignment will be done one line at a time. * So for now just interchange the Xcolor structure's black and white * if the -rv command line option was specified. */ if (paintjet && invert) swap_black_and_white(); /* For LaserJet, map each color to black or white based upon the * combined intensity of the RGB components. Note that the normal * non-reversed (-rv) LaserJet mapping will represent light areas * of the screen as black on the paper. */ if (!paintjet) for (n=xwd_header.ncolors, xcolor=xwd_colors, cmap=colormap; n>0; n--, xcolor++, cmap++) *cmap = (Intensity(xcolor->red, xcolor->green, xcolor->blue) < cutoff) ? (invert ? BLACK : WHITE) : (invert ? WHITE : BLACK); } reset_color_mapping(); } /* On a PaintJet printer, the programmable color intensity ranges are: * * red: 4..90 green: 4..88 blue: 6..85 * * The following macros map the 0..65535 intensity ranges of X colors * into the PaintJet's ranges. */ #define fixred(x) (x / 762 + 4) #define fixgreen(x) (x / 780 + 4) #define fixblue(x) (x / 829 + 6) #define is_grey(r,g,b) ((r == g) && (r == b)) static void select_grey (int level, int *r, int *g, int *b) { /* Forced selection of a grey. This is done since the PaintJet does * not do very well when picking greys, they tend to become pink! */ if (level > 66) { /* white */ *r = 90; *g = 88; *b = 85; } else if (level > 35) { *r = 43; *g = 43; *b = 45; } else if (level > 21) { *r = 25; *g = 25; *b = 33; } else if (level > 15) { *r = 15; *g = 16; *b = 18; } else if (level > 11) { *r = 14; *g = 14; *b = 18; } else if (level > 6) { *r = 6; *g = 7; *b = 8; } else { /* black */ *r = 4; *g = 4; *b = 6; } } static int load_printer_color ( long index, int nearmatch, enum device device) /* for non Direct/TrueColor */ { int n, red, blue, green, xred, xgreen, xblue; long compositeRGB; if (colormap[index] != EMPTY) /* printer has already been programmed for this color index */ return(1); /* "success" */ else { xred = xwd_colors[index].red; xgreen = xwd_colors[index].green; xblue = xwd_colors[index].blue; /* determine the scaled RGB PaintJet color values */ if (device == PJET) { red = fixred(xred); green = fixgreen(xgreen); blue = fixblue(xblue); if (is_grey(xred, xgreen, xblue)) /* assist grey selection */ select_grey(red, &red, &green, &blue); } compositeRGB = (red << 16) | (green << 8) | blue; /* search for a matching or unused PaintJet mapping entry */ for (n=0; nindex == i) { color.indexchain = current; return(current->pjcolor_index); /* found */ } current = current->next; } while (current != start); return(-1); /* not found */ } /* Calculate the individual and composite printer RGB values. (Only the * composite value is set for monochrome output.) */ static void select_printer_color ( long index, int *red, int *green, int *blue, long *compositeRGB, enum device device) { int xred, xgreen, xblue; xred = xwd_colors[((index & color.sm.Rmask) >> color.sm.Rshift)].red; xgreen = xwd_colors[((index & color.sm.Gmask) >> color.sm.Gshift)].green; xblue = xwd_colors[((index & color.sm.Bmask) >> color.sm.Bshift)].blue; if (color.PaintJet) { if (color.Invert) { if (RGBmatch((xred & fullintensity), (xgreen & fullintensity), (xblue & fullintensity), fullintensity)) xred = xgreen = xblue = 0; else if (RGBmatch(xred, xgreen, xblue, 0)) xred = xgreen = xblue = fullintensity; } /* determine the scaled RGB PaintJet color values */ if (device == PJET) { *red = fixred(xred); *green = fixgreen(xgreen); *blue = fixblue(xblue); if (is_grey(xred, xgreen, xblue)) /* assist grey selection */ select_grey(*red, red, green, blue); } if (device == PJETXL) { *red = xred >> 8; *green = xgreen >> 8; *blue = xblue >> 8; } *compositeRGB = (*red << 16) | (*green << 8) | *blue; } else /* monochrome */ *compositeRGB = (Intensity(xred, xgreen, xblue) < color.CutOff) ? (color.Invert ? BLACK : WHITE) : (color.Invert ? WHITE : BLACK); } /* Search for a color matching the compositeRGB value in the array of * colors already programmed into the printer. Returns 1 if found, * 0 otherwise. The matching array index is returned in pindex. */ static int color_already_in_printer (long compositeRGB, long *pindex) { int n; for (n=0; n> 16 & 0xFF) - (y >> 16 & 0xFF); long g = (x >> 8 & 0xFF) - (y >> 8 & 0xFF); long b = (x & 0xFF) - (y & 0xFF); return(r*r + g*g + b*b); } static long find_nearest_programmed_color (long compositeRGB) { int n, nearest = 0; long neardiff = composite_diff(pjcolor[0], compositeRGB); long diff; for (n=1; nnext; } /* put index values in the new cell */ new->index = cindex; new->pjcolor_index = pindex; /* link the new cell into the chain */ if (color.indexchain == NULL) new->next = new; else { new->next = color.indexchain->next; color.indexchain->next = new; } /* leave head pointer at the new cell */ color.indexchain = new; } static int load_printer_color_DT ( long index, int nearmatch, enum device device) /* for Direct/TrueColor */ { int pjred, pjgreen, pjblue; long compositeRGB; long pindex; if (lookup_color_index(index) >= 0) return(1); /* "success" */ else { select_printer_color(index, &pjred, &pjgreen, &pjblue, &compositeRGB, device); if (color_already_in_printer(compositeRGB, &pindex)) { add_index_to_chain(index, pindex); return(1); /* success */ } else if (program_new_printer_color(pjred, pjgreen, pjblue, compositeRGB, &pindex)) { add_index_to_chain(index, pindex); return(1); /* success */ } else if (nearmatch) { add_index_to_chain(index, find_nearest_programmed_color(compositeRGB)); return(0); /* failure, sorta... */ } } return(0); /* failure */ } static int load_line_colors ( long *line, int length, int nearmatch, enum device device) { int result = 1; /* initialized to "success" */ for (; length>0; length--, line++) { result &= Direct_or_TrueColor ? load_printer_color_DT(*line, nearmatch, device) : load_printer_color(*line, nearmatch, device); if (!(nearmatch || result)) break; } return(result); } static void download_colors ( long *line, int length, enum device device) { /* For the first attempt at loading the colors for a line only exact * color matches are accepted. If this fails, the closest colors are * accepted on the second attempt. * * Note: The first "if" test below bypasses the initial color loading * attempt for monochrome output (which will only come here for Direct * or TrueColor mono). This forces reset_color_mapping which is * necessary to keep the color index chain down to a tolerable length. */ if (!color.PaintJet || !load_line_colors(line, length, FALSE, device)) { reset_color_mapping(); if (!load_line_colors(line, length, TRUE, device) && !color_warning_given) { fprintf(stderr,(catgets(nlmsg_fd,NL_SETN,9, "%s: Warning: Cannot print all image colors.\n")), progname); color_warning_given = TRUE; } } } static _X_INLINE _X_NORETURN void invalid_depth_for_visual(int depth, const char *name) { fatal_err(catgets(nlmsg_fd,NL_SETN,25, "%d bit deep %s bitmap not supported.\n"), depth, name); } static void validate_visual(void) { int depth = xwd_header.pixmap_depth; switch (xwd_header.visual_class) { case GrayScale: if (depth > 8) invalid_depth_for_visual(depth, "GrayScale"); break; case StaticGray: if (depth > 8) invalid_depth_for_visual(depth, "StaticGray"); break; case PseudoColor: if (depth > 8) invalid_depth_for_visual(depth, "PseudoColor"); break; case StaticColor: if (depth > 8) invalid_depth_for_visual(depth, "StaticColor"); break; case DirectColor: if (depth != 12 && depth != 24) invalid_depth_for_visual(depth, "DirectColor"); break; case TrueColor: if (depth != 12 && depth != 24) invalid_depth_for_visual(depth, "TrueColor"); break; default: fatal_err((catgets(nlmsg_fd,NL_SETN,26, "visual class #%d not supported.\n")), (int)xwd_header.visual_class); } } static void read_xwd_data (FILE *in) { # define WINDOW_NAME_ALLOC 32 unsigned long swaptest = 1; int window_name_size; int image_size; int n; char window_name [WINDOW_NAME_ALLOC]; /* Read in XWDFileHeader structure */ if (fread((char*) &xwd_header, 1, XWDHEADERSIZE, in) != XWDHEADERSIZE) fatal_err((catgets(nlmsg_fd,NL_SETN,10, "Could not read xwd file's header."))); if (*(char *) &swaptest) _swaplong((char *) &xwd_header, XWDHEADERSIZE); validate_visual(); /* Skip over window name */ window_name_size = xwd_header.header_size - XWDHEADERSIZE; while (window_name_size > 0) { n = window_name_size > WINDOW_NAME_ALLOC ? WINDOW_NAME_ALLOC : window_name_size; if (fread(window_name, 1, n, in) != n) fatal_err((catgets(nlmsg_fd,NL_SETN,11, "Could not read xwd file's window name."))); window_name_size -= n; } /* Allocate space for xwd color structures */ if (!(xwd_colors = malloc(sizeof(XColor) * xwd_header.ncolors))) fatal_err((catgets(nlmsg_fd,NL_SETN,12, "Could not allocate memory for xwdfile color table."))); /* Read in xwd color structures */ for (n = 0; n < xwd_header.ncolors; n++) if (fread(&xwd_colors[n], 1, XCOLORSIZE, in) != XCOLORSIZE) fatal_err((catgets(nlmsg_fd,NL_SETN,13, "Could not read xwd file's color table."))); if (*(char *) &swaptest) { for (n = 0; n < xwd_header.ncolors; n++) { _swaplong((char *) &xwd_colors[n].pixel, sizeof(long)); _swapshort((char *) &xwd_colors[n].red, 3 * sizeof(short)); } } /* Allocate space for xwd image */ if (xwd_header.pixmap_format == ZPixmap) image_size = 1; else if (xwd_header.pixmap_format == XYPixmap) image_size = xwd_header.pixmap_depth; else fatal_err((catgets(nlmsg_fd,NL_SETN,14, "Image in xwd file is not in Z or XY pixmap format."))); image_size *= xwd_header.bytes_per_line * xwd_header.pixmap_height; if (!(xwd_image = malloc(image_size))) fatal_err((catgets(nlmsg_fd,NL_SETN,15, "Could not allocate memory for xwd file's image."))); /* Read in xwd image */ if (fread(xwd_image, 1, image_size, in) != image_size) fatal_err((catgets(nlmsg_fd,NL_SETN,16, "Could not read xwd file's image."))); } static void write_image_prefix ( FILE *out, int scale, int density, const char *header, enum device device, int position_on_page, int initial_formfeed, enum orientation orient, float gamma, int render, int slide) { if (initial_formfeed) fprintf(out,"\014"); /* Write out header & positioning commands */ if (header) { if (position_on_page) fprintf(out,"\033&a%dH\033&a%dV", /* headerloc x & y are written in decipoints */ (int) headerloc.x / 10, (int) headerloc.y / 10); fprintf(out,"%s\n", header); } /* Prepare printer for raster graphics: */ /* Write image positioning commands */ if (position_on_page) fprintf(out,"\033&a%dH\033&a%dV", /* imageloc x & y are written in decipoints */ (int) imageloc.x / 10, (int) imageloc.y / 10); /* If doing transparencies, tell the printer before raster graphics */ if (slide && device != LJET) fprintf(out, "\033&k3W"); /* Set printer resolution */ fprintf(out,"\033*t%dR", density); /* * do device dependent escape sequences */ if (device == PJET) { /* Enable all four "planes" for PaintJet */ fprintf(out,"\033*r4U"); /* Set picture width for PaintJet */ fprintf(out,"\033*r%dS", (int) (((orient == PORTRAIT) ? limit.width : limit.height) * scale)); } /* Enable various options for PaintJet XL */ if (device == PJETXL) { double dotsize; int n; /* Speed up printing by telling that there * will be no negative positioning */ fprintf(out, "\033&a1N"); if (gamma > 0.009) fprintf(out, "\033*t%.2fI", gamma); if (render > 0) fprintf(out, "\033*t%dJ", render); if (Direct_or_TrueColor) /* Enable direct by pixel for PaintJet XL */ fwrite("\033*v6W\000\003\010\010\010\010", 1, 11, out); else { /* Enable index by pixel for PaintJet XL */ fwrite("\033*v6W\000\001\010\010\010\010", 1, 11, out); /* Program the palette */ for (n = 0; n < xwd_header.ncolors; n++) { fprintf(out,"\033*v%dA", (xwd_colors[n].red >> 8)); fprintf(out,"\033*v%dB", (xwd_colors[n].green >> 8)); fprintf(out,"\033*v%dC", (xwd_colors[n].blue >> 8)); fprintf(out,"\033*v%dI", n); } } /**************************************** * * * PaintJet XL will do its own scaling * * * * Set picture width for PaintJet XL * ****************************************/ fprintf(out,"\033*r%dS", (int) ((orient == PORTRAIT) ? xwd_header.pixmap_width : xwd_header.pixmap_height)); fprintf(out,"\033*r%dT", (int) ((orient == PORTRAIT) ? xwd_header.pixmap_height : xwd_header.pixmap_width)); dotsize = dot_centipoints(scale, density); fprintf(out,"\033*t%dH", (int)(((orient == PORTRAIT) ? xwd_header.pixmap_width : xwd_header.pixmap_height) * dotsize / 10)); fprintf(out,"\033*t%dV", (int)(((orient == PORTRAIT) ? xwd_header.pixmap_height : xwd_header.pixmap_width) * dotsize / 10)); } /* Switch to raster graphics mode */ if (device != PJETXL) fprintf(out,"\033*r1A"); else fprintf(out,"\033*r3A"); } static void write_image_suffix ( FILE *out, const char *trailer, int position_on_page, int slide, int render, enum device device) { /* Exit raster graphics mode */ if (device == PJETXL) fprintf(out,"\033*rC"); else { fprintf(out,"\033*rB"); fprintf(out,"\033E"); } /* If doing transparencies, tell it to stop */ if (slide && device != LJET) fprintf(out, "\033&k1W"); if (device == PJETXL) { /* If selected a rendering algorithm, tell it to stop */ if (render) fprintf(out, "\033*t3J"); fprintf(out, "\033&a0N"); } /* Write out trailer & positioning commands */ if (trailer) { if (position_on_page) fprintf(out,"\033&a%dH\033&a%dV", /* trailerloc x & y are written in decipoints */ (int) trailerloc.x / 10, (int) trailerloc.y / 10); fprintf(out,"%s\n", trailer); } } static unsigned long Z_image_pixel (int x, int y) { int pixel_bytes, offset; unsigned char *image; unsigned long pixel; pixel_bytes = xwd_header.bits_per_pixel >> 3; offset = ((xwd_header.bits_per_pixel == 4) ? (x / 2) : ((xwd_header.bits_per_pixel == 1) ? (x / 8) : (x * pixel_bytes))) + (y * xwd_header.bytes_per_line); image = (unsigned char *) &xwd_image[offset]; switch (pixel_bytes) { case 0: /* pixel per nibble or bit per pixel packing */ if (xwd_header.bits_per_pixel == 1) { if (xwd_header.byte_order == MSBFirst) pixel = *image >> (7 - (x % 8)); else pixel = *image >> (x % 8); } else { /* xwd_header.bits_per_pixel == 4 */ if (xwd_header.byte_order == MSBFirst) pixel = (x & 1) ? *image : (*image >> 4); else pixel = (x & 1) ? (*image >> 4) : *image; } break; case 1: pixel = *image; break; case 2: pixel = (xwd_header.byte_order == MSBFirst) ? ((unsigned long)*image << 8 | *(image + 1)) : (*image | (unsigned long)*(image + 1) << 8); break; case 3: pixel = (xwd_header.byte_order == MSBFirst) ? ((unsigned long)*image << 16 | (unsigned long)*(image + 1) << 8 | (unsigned long)*(image + 2)) : (*image | (unsigned long)*(image + 1) << 8 | (unsigned long)*(image + 2) << 16); break; case 4: pixel = (xwd_header.byte_order == MSBFirst) ? ((unsigned long)*image << 24 | (unsigned long)*(image+1) << 16 | (unsigned long)*(image+2) << 8 | *(image+3)) : (*image | (unsigned long)*(image+1) << 8 | (unsigned long)*(image+2) << 16 | (unsigned long)*(image+3) << 24); break; default: pixel = 0; break; } return (pixel & Z_pixel_mask); } static unsigned long XY_image_pixel (int x, int y) { int plane_start, line_start, bytes_per_bitmap_unit, bitmap_unit_start, byte_within_bitmap_unit, offset, byte_mask; plane_start = (xwd_header.pixmap_depth - 1) * xwd_header.pixmap_height * xwd_header.bytes_per_line; line_start = xwd_header.bytes_per_line * y; bytes_per_bitmap_unit = xwd_header.bitmap_unit >> 3; bitmap_unit_start = (x / xwd_header.bitmap_unit) * bytes_per_bitmap_unit; byte_within_bitmap_unit = (xwd_header.byte_order == MSBFirst) ? (x % xwd_header.bitmap_unit) >> 3 : bytes_per_bitmap_unit - ((x % xwd_header.bitmap_unit) >> 3) - 1; offset = plane_start + line_start + bitmap_unit_start + byte_within_bitmap_unit; byte_mask = (xwd_header.bitmap_bit_order == MSBFirst) ? 0x80 >> (x % 8) : 0x01 << (x % 8); return(xwd_image[offset] & byte_mask ? 1 : 0); } static void direct_by_pixel( FILE *out, long *line, int length, enum device device) { int red, green, blue; long compositeRGB; fprintf(out, "\033*b%dW", length * 3); for (; length>0; length--, line++) { select_printer_color(*line, &red, &green, &blue, &compositeRGB, device); fprintf(out, "%c%c%c", (char) red, (char) green, (char) blue); } } static void index_by_pixel( FILE *out, long *line, int length) { register int n; long *lp; char *line_pixels; register char *lc; if (!(line_pixels = malloc(length))) fatal_err((catgets(nlmsg_fd,NL_SETN,17, "Could not allocate raster line memory."))); for (n=0, lc=line_pixels, lp=line; n 0) { bit = (*lc++ >> p) & 0x01; e = scale; while (e--) { byte = (byte << 1) | bit; bytebits++; if (bytebits == 8) { *rl++ = byte; bytebits = 0; } } } if (bytebits) *rl = byte << (8 - bytebits); } e = scale; while (e--) { for (p=0; p=0; y--) *lp++ = Z_image_pixel(x,y); write_raster_line(out, scale, device, line, width); } } static void write_portrait_XY_image ( FILE *out, int scale, enum device device) { int x, y; int width = limit.width; int height = limit.height; long *line, *lp; if (!(line = malloc(width * sizeof(long)))) fatal_err((catgets(nlmsg_fd,NL_SETN,20, "Could not allocate memory for image line buffer."))); for (y=0; y=0; y--) *lp++ = XY_image_pixel(x,y); write_raster_line(out, scale, device, line, width); } } static void write_Z_image ( FILE *out, int scale, enum orientation orient, enum device device) { if (orient == PORTRAIT) { write_portrait_Z_image(out, scale, device); } else write_landscape_Z_image(out, scale, device); } static void write_XY_image ( FILE *out, int scale, enum orientation orient, enum device device) { if (xwd_header.pixmap_depth > 1) fatal_err((catgets(nlmsg_fd,NL_SETN,22, "XY format image, multiplane images must be Z format."))); if (orient == PORTRAIT) { write_portrait_XY_image(out, scale, device); } else write_landscape_XY_image(out, scale, device); } static void write_image ( FILE *out, int scale, enum orientation orient, enum device device) { switch (xwd_header.pixmap_format) { case XYPixmap: write_XY_image(out, scale, orient, device); break; case ZPixmap: write_Z_image(out, scale, orient, device); break; default: fatal_err((catgets(nlmsg_fd,NL_SETN,23, "image not in XY or Z format."))); } } void x2jet( FILE *in, FILE *out, int scale, int density, int width, int height, int left, int top, /* in 300ths of an inch */ const char *header, const char *trailer, enum orientation orient, int invert, int initial_formfeed, int position_on_page, int slide, enum device device, unsigned int cutoff, float gamma, int render) { int paintjet = FALSE; true_scale = scale; if (device != LJET) paintjet = TRUE; read_xwd_data(in); Z_pixel_mask = ~(0xFFFFFFFFUL << xwd_header.pixmap_depth); prepare_color_mapping(invert, paintjet, cutoff, out); scale_and_orient_image(&scale, &density, width, height, left, top, header, trailer, &orient, position_on_page, device); write_image_prefix(out, scale, density, header, device, position_on_page, initial_formfeed, orient, gamma, render, slide); write_image(out, scale, orient, device); write_image_suffix(out, trailer, position_on_page, slide, render, device); fclose(out); } static void fatal_err (const char *s, ...) { va_list ap; fprintf(stderr, "%s: ", progname); va_start(ap, s); vfprintf(stderr, s, ap); va_end(ap); fputc('\n', stderr); exit(EXIT_FAILURE); }