/* Copyright (C) 1989, 1992, 1995 artofcode LLC. 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., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevepsc.c,v 1.3.2.1.2.1 2003/01/17 00:49:00 giles Exp $*/ /* Epson color dot-matrix printer driver by dave@exlog.com */ #include "gdevprn.h" /* * For 9-pin printers, you may select * X_DPI = 60, 120, or 240 * Y_DPI = 60 or 72 * For 24-pin printers, you may select * X_DPI = 60, 120, 180, 240, or 360 * Y_DPI = 60, 72, 180, or 216 * Note that a given printer implements *either* Y_DPI = 60 | 180 *or* * Y_DPI = 72 | 216; no attempt is made to check this here. * Note that X_DPI = 180 or 360 requires Y_DPI > 100; * this isn't checked either. Finally, note that X_DPI=240 and * X_DPI=360 are double-density modes requiring two passes to print. * * The values of X_DPI and Y_DPI may be set at compile time: * see gdevs.mak. * * At some time in the future, we could simulate 24-bit output on * 9-pin printers by using fractional vertical positioning; * we could even implement an X_DPI=360 mode by using the * ESC++ command that spaces vertically in units of 1/360" * (not supported on many printers.) */ #ifndef X_DPI # define X_DPI 180 /* pixels per inch */ #endif #ifndef Y_DPI # define Y_DPI 180 /* pixels per inch */ #endif /* ** Colors for EPSON LQ-2550. ** ** We map VIOLET to BLUE since this is the best we can do. */ #define BLACK 0 #define MAGENTA 1 #define CYAN 2 #define VIOLET 3 #define YELLOW 4 #define RED 5 #define GREEN 6 #define WHITE 7 /* ** The offset in this array correspond to ** the ESC-r n value */ static char rgb_color[2][2][2] = { BLACK, VIOLET, GREEN, CYAN, RED, MAGENTA, YELLOW, WHITE, }; /* Map an RGB color to a printer color. */ #define cv_shift (sizeof(gx_color_value) * 8 - 1) private gx_color_index epson_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g, gx_color_value b) { if (gx_device_has_color(dev)) { /* use ^7 so WHITE is 0 for internal calculations */ return (gx_color_index)rgb_color[r >> cv_shift][g >> cv_shift][b >> cv_shift] ^ 7; } else { return gx_default_map_rgb_color(dev, r, g, b); } } /* Map the printer color back to RGB. */ private int epson_map_color_rgb(gx_device *dev, gx_color_index color, gx_color_value prgb[3]) { #define c1 gx_max_color_value if (gx_device_has_color(dev)) switch ((ushort)color ^ 7) { case BLACK: prgb[0] = 0; prgb[1] = 0; prgb[2] = 0; break; case VIOLET: prgb[0] = 0; prgb[1] = 0; prgb[2] = c1; break; case GREEN: prgb[0] = 0; prgb[1] = c1; prgb[2] = 0; break; case CYAN: prgb[0] = 0; prgb[1] = c1; prgb[2] = c1; break; case RED: prgb[0] = c1; prgb[1] = 0; prgb[2] = 0; break; case MAGENTA: prgb[0] = c1; prgb[1] = 0; prgb[2] = c1; break; case YELLOW: prgb[0] = c1; prgb[1] = c1; prgb[2] = 0; break; case WHITE: prgb[0] = c1; prgb[1] = c1; prgb[2] = c1; break; } else return gx_default_map_color_rgb(dev, color, prgb); return 0; } /* The device descriptor */ private dev_proc_print_page(epsc_print_page); private gx_device_procs epson_procs = prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close, epson_map_rgb_color, epson_map_color_rgb); const gx_device_printer far_data gs_epsonc_device = prn_device(epson_procs, "epsonc", DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS, X_DPI, Y_DPI, 0, 0, 0.25, 0, /* margins */ 3, epsc_print_page); /* ------ Internal routines ------ */ /* Forward references */ private void epsc_output_run(P6(byte *, int, int, char, FILE *, int)); /* Send the page to the printer. */ #define DD 0x80 /* double density flag */ private int epsc_print_page(gx_device_printer *pdev, FILE *prn_stream) { static char graphics_modes_9[5] = { -1, 0 /*60*/, 1 /*120*/, -1, DD+3 /*240*/ }; static char graphics_modes_24[7] = { -1, 32 /*60*/, 33 /*120*/, 39 /*180*/, -1, -1, DD+40 /*360*/ }; int y_24pin = pdev->y_pixels_per_inch > 72; int y_mult = (y_24pin ? 3 : 1); int line_size = (pdev->width + 7) >> 3; /* always mono */ int in_size = line_size * (8 * y_mult); byte *in = (byte *)gs_malloc(in_size+1, 1, "epsc_print_page(in)"); int out_size = ((pdev->width + 7) & -8) * y_mult; byte *out = (byte *)gs_malloc(out_size+1, 1, "epsc_print_page(out)"); int x_dpi = pdev->x_pixels_per_inch; char start_graphics = (y_24pin ? graphics_modes_24 : graphics_modes_9)[x_dpi / 60]; int first_pass = (start_graphics & DD ? 1 : 0); int last_pass = first_pass * 2; int dots_per_space = x_dpi / 10; /* pica space = 1/10" */ int bytes_per_space = dots_per_space * y_mult; int skip = 0, lnum = 0, pass; /* declare color buffer and related vars */ byte *color_in; int color_line_size, color_in_size; int spare_bits = (pdev->width % 8); /* left over bits to go to margin */ int whole_bits = pdev->width - spare_bits; /* Check allocations */ if ( in == 0 || out == 0 ) { if ( in ) gs_free((char *)in, in_size+1, 1, "epsc_print_page(in)"); if ( out ) gs_free((char *)out, out_size+1, 1, "epsc_print_page(out)"); return -1; } /* Initialize the printer and reset the margins. */ fwrite("\033@\033P\033l\000\033Q\377\033U\001\r", 1, 14, prn_stream); /* Create color buffer */ if (gx_device_has_color(pdev)) { color_line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev); color_in_size = color_line_size * (8 * y_mult); if((color_in = (byte *)gs_malloc(color_in_size+1, 1, "epsc_print_page(color)")) == 0) { gs_free((char *)in, in_size+1, 1, "epsc_print_page(in)"); gs_free((char *)out, out_size+1, 1, "epsc_print_page(out)"); return(-1); } } else { color_in = in; color_in_size = in_size; color_line_size = line_size; } /* Print lines of graphics */ while ( lnum < pdev->height ) { int lcnt; byte *nextcolor = NULL; /* position where next color appears */ byte *nextmono = NULL; /* position to map next color */ /* Copy 1 scan line and test for all zero. */ gdev_prn_copy_scan_lines(pdev, lnum, color_in, color_line_size); if ( color_in[0] == 0 && !memcmp((char *)color_in, (char *)color_in + 1, color_line_size - 1) ) { lnum++; skip += 3 / y_mult; continue; } /* Vertical tab to the appropriate position. */ while ( skip > 255 ) { fputs("\033J\377", prn_stream); skip -= 255; } if ( skip ) fprintf(prn_stream, "\033J%c", skip); /* Copy the rest of the scan lines. */ lcnt = 1 + gdev_prn_copy_scan_lines(pdev, lnum + 1, color_in + color_line_size, color_in_size - color_line_size); if ( lcnt < 8 * y_mult ) { memset((char *)(color_in + lcnt * color_line_size), 0, color_in_size - lcnt * color_line_size); if (gx_device_has_color(pdev)) /* clear the work buffer */ memset((char *)(in + lcnt * line_size), 0, in_size - lcnt * line_size); } /* ** We need to create a normal epson scan line from our color scan line ** We do this by setting a bit in the "in" buffer if the pixel byte is set ** to any color. We then search for any more pixels of that color, setting ** "in" accordingly. If any other color is found, we save it for the next ** pass. There may be up to 7 passes. ** In the future, we should make the passes so as to maximize the ** life of the color ribbon (i.e. go lightest to darkest). */ do { byte *inp = in; byte *in_end = in + line_size; byte *out_end = out; byte *out_blk; register byte *outp; if (gx_device_has_color(pdev)) { register i,j; register byte *outbuf, *realbuf; byte current_color; int end_next_bits = whole_bits; int lastbits; /* Move to the point in the scanline that has a new color */ if (nextcolor) { realbuf = nextcolor; outbuf = nextmono; memset((char *)in, 0, (nextmono - in)); i = nextcolor - color_in; nextcolor = NULL; end_next_bits = (i / color_line_size) * color_line_size + whole_bits; } else { i = 0; realbuf = color_in; outbuf = in; nextcolor = NULL; } /* move thru the color buffer, turning on the appropriate ** bit in the "mono" buffer", setting pointers to the next ** color and changing the color output of the epson */ for (current_color = 0; i <= color_in_size && outbuf < in + in_size; outbuf++) { /* Remember, line_size is rounded up to next whole byte ** whereas color_line_size is the proper length ** We only want to set the proper bits in the last line_size byte. */ if (spare_bits && i == end_next_bits) { end_next_bits = whole_bits + i + spare_bits; lastbits = 8 - spare_bits; } else lastbits = 0; for (*outbuf = 0, j = 8; --j >= lastbits && i <= color_in_size; realbuf++,i++) { if (*realbuf) { if (current_color > 0) { if (*realbuf == current_color) { *outbuf |= 1 << j; *realbuf = 0; /* throw this byte away */ } /* save this location for next pass */ else if (nextcolor == NULL) { nextcolor = realbuf - (7 - j); nextmono = outbuf; } } else { *outbuf |= 1 << j; current_color = *realbuf; /* set color */ *realbuf = 0; } } } } *outbuf = 0; /* zero the end, for safe keeping */ /* Change color on the EPSON, current_color must be set ** but lets check anyway */ if (current_color) fprintf(prn_stream,"\033r%d",current_color ^ 7); } /* We have to 'transpose' blocks of 8 pixels x 8 lines, */ /* because that's how the printer wants the data. */ /* If we are in a 24-pin mode, we have to transpose */ /* groups of 3 lines at a time. */ if ( y_24pin ) { for ( ; inp < in_end; inp++, out_end += 24 ) { gdev_prn_transpose_8x8(inp, line_size, out_end, 3); gdev_prn_transpose_8x8(inp + line_size * 8, line_size, out_end + 1, 3); gdev_prn_transpose_8x8(inp + line_size * 16, line_size, out_end + 2, 3); } /* Remove trailing 0s. */ while ( out_end > out && out_end[-1] == 0 && out_end[-2] == 0 && out_end[-3] == 0 ) out_end -= 3; } else { for ( ; inp < in_end; inp++, out_end += 8 ) { gdev_prn_transpose_8x8(inp, line_size, out_end, 1); } /* Remove trailing 0s. */ while ( out_end > out && out_end[-1] == 0 ) out_end--; } for ( pass = first_pass; pass <= last_pass; pass++ ) { for ( out_blk = outp = out; outp < out_end; ) { /* Skip a run of leading 0s. */ /* At least 10 are needed to make tabbing worth it. */ /* We do everything by 3's to avoid having to make */ /* different cases for 9- and 24-pin. */ if ( *outp == 0 && outp + 12 <= out_end && outp[1] == 0 && outp[2] == 0 && (outp[3] | outp[4] | outp[5]) == 0 && (outp[6] | outp[7] | outp[8]) == 0 && (outp[9] | outp[10] | outp[11]) == 0 ) { byte *zp = outp; int tpos; byte *newp; outp += 12; while ( outp + 3 <= out_end && *outp == 0 && outp[1] == 0 && outp[2] == 0 ) outp += 3; tpos = (outp - out) / bytes_per_space; newp = out + tpos * bytes_per_space; if ( newp > zp + 10 ) { /* Output preceding bit data. */ if ( zp > out_blk ) /* only false at */ /* beginning of line */ epsc_output_run(out_blk, (int)(zp - out_blk), y_mult, start_graphics, prn_stream, pass); /* Tab over to the appropriate position. */ fprintf(prn_stream, "\033D%c%c\t", tpos, 0); out_blk = outp = newp; } } else outp += y_mult; } if ( outp > out_blk ) epsc_output_run(out_blk, (int)(outp - out_blk), y_mult, start_graphics, prn_stream, pass); fputc('\r', prn_stream); } } while (nextcolor); skip = 24; lnum += 8 * y_mult; } /* Eject the page and reinitialize the printer */ fputs("\f\033@", prn_stream); gs_free((char *)out, out_size+1, 1, "epsc_print_page(out)"); gs_free((char *)in, in_size+1, 1, "epsc_print_page(in)"); if (gx_device_has_color(pdev)) gs_free((char *)color_in, color_in_size+1, 1, "epsc_print_page(rin)"); return 0; } /* Output a single graphics command. */ /* pass=0 for all columns, 1 for even columns, 2 for odd columns. */ private void epsc_output_run(byte *data, int count, int y_mult, char start_graphics, FILE *prn_stream, int pass) { int xcount = count / y_mult; fputc(033, prn_stream); if ( !(start_graphics & ~3) ) { fputc("KLYZ"[start_graphics], prn_stream); } else { fputc('*', prn_stream); fputc(start_graphics & ~DD, prn_stream); } fputc(xcount & 0xff, prn_stream); fputc(xcount >> 8, prn_stream); if ( !pass ) fwrite((char *)data, 1, count, prn_stream); else { /* Only write every other column of y_mult bytes. */ int which = pass; byte *dp = data; register int i, j; for ( i = 0; i < xcount; i++, which++ ) for ( j = 0; j < y_mult; j++, dp++ ) { putc(((which & 1) ? *dp : 0), prn_stream); } } }