summaryrefslogtreecommitdiff
path: root/devices/gdevlp8k.c
diff options
context:
space:
mode:
Diffstat (limited to 'devices/gdevlp8k.c')
-rw-r--r--devices/gdevlp8k.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/devices/gdevlp8k.c b/devices/gdevlp8k.c
new file mode 100644
index 000000000..2e6d178cc
--- /dev/null
+++ b/devices/gdevlp8k.c
@@ -0,0 +1,397 @@
+/* Copyright (C) 2001-2012 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134, San Rafael,
+ CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* EPSON LP-8000 ESC-sequence Laser Printer driver for Ghostscript.
+
+This driver structure is most close to that of the Epson 'ESC/P 2' language
+printer driver "gdevescp.c" contributed by Richard Brown, but all the control
+sequences and data formats are totally different.
+
+The main driver strategy is as follows. The driver scans lines, skips empty
+ones, removes leading and trailing zeros for other lines, compresses the
+non-zero rest of each line and finally outputs the data.
+
+At the moment the driver supports only 300x300 DPI resolution. If somebody
+needs 240x240, another valid value for LP-8000 printer, he or she can try to
+play with the corresponding values in initialization and termination
+strings. Or I shall spend some extra time for hacking, if enough people
+encourage me to do it. (The only available in our laboratory "Operation
+guide" in Japanese does not contain any information about it. And LP-8000
+driver for Japanese Windows does not support this mode either.)
+
+The output data format is the following.
+
+1. Initialization string, pretty long and sophisticated, I don't know why it
+was necessary.
+
+2. Data bits for each line. The most general format includes both starting X
+and Y values as well as data type (simple or compressed).
+
+3. Termination string.
+
+ DATA FORMATS
+
+1. A simple (non-compressed) data format. By evident reasons it is NOT
+SUPPORTED by the driver and is discussed here just as a starting point for
+the future explanations. "\035" here is an alias for 0x1d ESC-character :
+
+"\035" "Starting X point in ASCII format" "X"
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "Number of data BYTES for this printer line in ASCII format" ";"
+"Number of POINTS to print in this line (equals to the
+(Number of BYTES)*8)" ";"
+"1;obi{I" "data BYTES for this line in BINARY format"
+
+Both X and Y printer coordinates are 60 pixels shifted from the corresponding
+coordinates of the Ghostscript display, that is X = x - 60, Y = y - 60. For
+example, 1 inch left margin requires the value of 300 - 60 = 240 for
+starting X printer coordinate. Similar, 1.5 inch top margin requires Y
+values to start from 300*1.5 - 60 = 390.
+
+The shortest possible abbreviation for the simple data format string is
+
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "Number of data BYTES for this printer line in ASCII format" ";"
+"Number of POINTS to print in this line (equals to the
+(Number of BYTES)*8)" ";"
+"1;obi{I" "data BYTES for this line in BINARY format"
+
+In this case the value of the starting X point is assumed to be equal to
+that for the previous line.
+
+An example of the data output for 2 printer lines
+
+"\035"315X"\035"240Y"\035"2;16;1;obi{I"0ff0""\035"241Y"\035"3;24;1;obi{I"0f000f"
+
+Here "0ff0" is an alias for 0x0f 0xf0 binary data, etc. The first line of the
+above example starts from X=315, Y=240 and consists of 2 data bytes
+resulting in 4 blank (white) points followed by 8 black points followed by 4
+white points on the paper. The second line starts from X=315, Y=241 and
+contains 3 data bytes resulting in output of 4 white, 4 black, 12 white and
+finally 4 black points.
+
+2. Compressed data format (SUPPORTED BY THE DRIVER).
+
+General description is as follows.
+
+"\035" "Starting X point in ASCII format" "X"
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "3bcI"
+"\035" "Total number of compressed BYTES in ASCII format" ";"
+"Number of POINTS to print in this line" ";"
+"1;obi{I" "compressed data BYTES for this line in BINARY format"
+"\035" "0bcI"
+
+Additional ESC-sequences "\035" "3bcI" and "\035" "0bcI" mean start and end
+of the compressed data format, respectively. As in the discussed above case
+of a non-compressed data format, the shortest abbreviation has the form of
+
+"\035" "Starting Y point in ASCII format" "Y"
+"\035" "Total number of compressed BYTES in ASCII format" ";"
+"Number of POINTS to print in this line" ";"
+"1;obi{I" "compressed data BYTES for this line in BINARY format"
+
+COMPRESSED DATA BYTES FORMAT has the form of
+
+"d1 d2 d3 d4 d4 count_d4 d5 d6 d6 count_d6 ... d(n-1) d(n-1) count_d(n-1) dn"
+
+Here dx (x = 1 ... n) means data in a BINARY format. Any 2 repeated bytes
+MUST follow by the count, otherwise the printer will interpret the next
+data byte as a counter. The count value indicates how many bytes of the
+same value should be INSERTED after the repeated ones. So, the total number of
+repeated bytes is (count + 2), not count. If there are only 2 equal data
+bytes somewhere in the data stream, they MUST follow by zero.
+
+Example of 2 compressed data strings.
+
+"\035"105X"\035"320Y"\035"3bcI"\035"3;2048;1;obi{I"0000fe"
+"\035"105X"\035"321Y"\035"11;2048;1;obi{I"0000021fffffe5fc000011"
+
+The first one containing 3 bytes of compressed data will result in empty
+(zero) line of 2048 blank points started from X=105, Y=320. The second one
+containing 11 compressed data bytes will produce the picture of 4*8 + 3 = 35
+white points followed by 5 + 16 + 0xe5*8 + 6 = 1859 black points followed by
+2 + 8*19 = 154 white points (total 2048 points) started from X=105, Y=321.
+
+Strictly speaking, it was not necessary to adjust the number of points to
+the byte boundary. I did it for the sake of simplicity. One more argument in
+favor of this step is that the error of positioning does not exceed (7 /
+300) inches or (7 / 118) cm, that is 0.6 mm, which is negligible, I guess.
+
+ADDITIONAL INFORMATION
+
+It is also possible to use LP-8000 printer with 180x180 DPI resolution as an
+"ibmpro" device from gdevepsn.c The only thing which should be corrected, is
+the value 0x30 in static const char ibmpro_init_string[]. Decimal 36
+fixes the 1,5 times elongation along the vertical axis. It is also
+recommended to choose the appropriate values for all margins. In my case it
+was 0.2, 0.6, 0, 0.3 in the device descriptor instead of the 0.2, 0.95, 0,
+1.0
+
+Nevertheless, typical Latex file looked so ugly after printing in this mode,
+that I preferred to spend several days for hacking the format of the Japanese
+Windows printer output for 300 DPI resolution and create my own driver.
+
+Any suggestions, corrections, critical comments, etc. are welcome!
+
+Oleg Fat'yanov <faty1@rlem.titech.ac.jp>
+
+*/
+
+#include "gdevprn.h"
+
+#ifndef X_DPI
+#define X_DPI 300
+#endif
+
+#ifndef Y_DPI
+#define Y_DPI 300
+#endif
+
+#define L_MARGIN 0.25
+#define B_MARGIN 0.25
+#define R_MARGIN 0.25
+#define T_MARGIN 0.25
+
+static dev_proc_print_page(lp8000_print_page);
+
+gx_device_printer far_data gs_lp8000_device =
+ prn_device(prn_bg_procs, "lp8000", /* The print_page proc is compatible with allowing bg printing */
+ DEFAULT_WIDTH_10THS,
+ DEFAULT_HEIGHT_10THS,
+ X_DPI, Y_DPI,
+ L_MARGIN, B_MARGIN, R_MARGIN, T_MARGIN,
+ 1, lp8000_print_page);
+
+static int
+lp8000_print_page(gx_device_printer *pdev, FILE *prn_stream)
+{
+
+ int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
+ int in_size = line_size;
+
+ byte *buf1 = (byte *)gs_malloc(pdev->memory, in_size, 1, "lp8000_print_page(buf1)");
+ byte *buf2 = (byte *)gs_malloc(pdev->memory, in_size, 1, "lp8000_print_page(buf2)");
+ byte *in = buf1;
+ byte *out = buf2;
+
+ int lnum, top, bottom, left, width;
+ int count, left1, left2, left0;
+
+/* Check memory allocations */
+
+ if ( buf1 == 0 || buf2 == 0 )
+ { if ( buf1 )
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "lp8000_print_page(buf1)");
+
+ if ( buf2 )
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "lp8000_print_page(buf2)");
+
+ return_error(gs_error_VMerror);
+ }
+
+/* Initialize the printer */
+
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+ fwrite("@EJL EN LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("\035rhE\033\001@EJL \n",1,12,prn_stream);
+ fwrite("@EJL SE LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("@EJL SET PU=1 PS=A4 ZO=OFF\n",1,27,prn_stream);
+ fwrite("@EJL EN LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("\0350;0.24muE\0352;300;300drE",1,23,prn_stream);
+ fwrite("\0350;300;300drE\0351tsE\0351mmE",1,23,prn_stream);
+ fwrite("\0357isE\0355iaF\0355ipP\03514psE\0350poE",1,26,prn_stream);
+ fwrite("\03560;60loE\0350X\0350Y",1,15,prn_stream);
+ fwrite("\0350;0;2360;3388caE",1,17,prn_stream);
+ fwrite("\0351cmE\0350alfP",1,11,prn_stream);
+ fwrite("\0350affP\0350boP\0350abP",1,16,prn_stream);
+ fwrite("\0354ilG\0350bcI\0350sarG",1,16,prn_stream);
+ fwrite("\0351;0;100spE\0352owE",1,16,prn_stream);
+
+/* Here the common part of the initialization string ends */
+
+/* Calculate the PRINTER_LEFT_MARGIN = device_left_margin - 60 adjusted to
+the byte boundary. Save this value for future comparison and set the
+starting X value of the printer line.
+*/
+ left1 = (int) (L_MARGIN * pdev->x_pixels_per_inch) - 60;
+ left1 = (left1 >> 3) << 3;
+ left0 = left1;
+
+ fwrite("\035",1,1,prn_stream);
+ fprintf(prn_stream,"%d",left1);
+ fwrite("X",1,1,prn_stream);
+
+ /* Set the compressed data format */
+ fwrite("\0353bcI",1,5,prn_stream);
+
+ top = T_MARGIN * pdev->y_pixels_per_inch;
+ bottom = pdev->height - B_MARGIN * pdev->y_pixels_per_inch;
+
+ left = ( (int) (L_MARGIN * pdev->x_pixels_per_inch) ) >> 3 ;
+ width = ((pdev->width - (int)(R_MARGIN * pdev->x_pixels_per_inch)) >> 3) - left;
+
+ /*
+ ** Print the page:
+ */
+
+ for ( lnum = top; lnum < bottom ; )
+
+ {
+ byte *in_data;
+ byte *inp;
+ byte *in_end;
+ byte *outp;
+ register byte *p, *q;
+ int lcnt;
+
+ /*
+ ** Check buffer for 0 data.
+ */
+
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ while ( in_data[0] == 0 &&
+ !memcmp((char *)in_data, (char *)in_data + 1, line_size - 1) &&
+ lnum < bottom )
+ {
+ lnum++;
+ gdev_prn_get_bits(pdev, lnum, in, &in_data);
+ }
+
+ if(lnum == bottom ) break;
+ /* finished with this page */
+
+ lcnt = gdev_prn_copy_scan_lines(pdev, lnum, in, in_size);
+
+ inp = in + left;
+ in_end = inp + width;
+
+/* Remove trailing 0s form the scan line data */
+
+ while (in_end > inp && in_end[-1] == 0)
+ {
+ in_end--;
+ }
+
+/* Remove leading 0s form the scan line data */
+
+ for(left2 = 0; inp < in_end && inp[0] == 0; inp++,left2++);
+
+/* Recalculate starting X value */
+
+ left2 = left1 + (left2 << 3);
+
+/* Compress non-zero data for this line*/
+
+ outp = out;
+
+ for( p = inp, q = inp + 1 ; q < in_end ; )
+ {
+ if( *p != *q++ )
+ {
+ /*
+ Copy non-repeated bytes
+ to the output buffer
+ */
+ *outp++ = *p++;
+ }
+ else
+ {
+ for (count = 2; ( *p == *q ) && (q < in_end); q++, count++);
+
+ /*
+ Copy repeated bytes and counts to the output buffer.
+ As long as count is <= 255, additional step is necessary
+ for a long repeated sequence
+ */
+
+ while (count > 257)
+ {
+ *outp++ = *p;
+ *outp++ = *p;
+ *outp++ = 255;
+ p += 257;
+ count -=257;
+ }
+ *outp++ = *p;
+ *outp++ = *p;
+ *outp++ = count - 2;
+ p += count;
+ q = p+1;
+ }
+ }
+
+/* The next line is necessary just in case of a single non-repeated byte at
+the end of the input buffer */
+
+if (p == (in_end - 1)) *outp++ = *p;
+
+/* End of the compression procedure */
+
+/* Set a new value of the starting X point, if necessary */
+
+if (left2 != left0)
+ {
+ left0 = left2;
+ fwrite("\035",1,1,prn_stream);
+ fprintf(prn_stream,"%d",left2);
+ fwrite("X",1,1,prn_stream);
+ }
+
+/* Output the data string to the printer.
+Y coordinate of the printer equals (lnum - 60)
+*/
+
+ fwrite("\035",1,1,prn_stream);
+ fprintf(prn_stream,"%d",lnum-60);
+ fwrite("Y\035",1,2,prn_stream);
+ fprintf(prn_stream,"%d;",(outp - out));
+ fprintf(prn_stream,"%d;",(in_end - inp) << 3);
+ fwrite("1;0bi{I",1,7,prn_stream);
+ fwrite(out,1,(outp - out),prn_stream);
+
+ lnum++;
+
+ }
+
+/* Send the termination string */
+
+ fwrite("\0350bcI",1,5,prn_stream);
+ fwrite("\0351coO",1,5,prn_stream);
+ fwrite("\035rhE",1,4,prn_stream);
+
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+ fwrite("@EJL SE LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("@EJL SET PU=1 PS=A4 ZO=OFF\n",1,27,prn_stream);
+ fwrite("@EJL EN LA=ESC/PAGE\n",1,20,prn_stream);
+ fwrite("\0350;0.24muE\0352;300;300drE",1,23,prn_stream);
+ fwrite("\0350;300;300drE\0351tsE\0351mmE",1,23,prn_stream);
+ fwrite("\0357isE\0355iaF\0355ipP\03514psE\0350poE",1,26,prn_stream);
+ fwrite("\03560;60loE\0350X\0350Y",1,15,prn_stream);
+ fwrite("\0350;0;2360;3388caE",1,17,prn_stream);
+ fwrite("\0351cmE\0350alfP",1,11,prn_stream);
+ fwrite("\0350affP\0350boP\0350abP",1,16,prn_stream);
+ fwrite("\0354ilG\0350bcI\0350sarG",1,16,prn_stream);
+ fwrite("\035rhE",1,4,prn_stream);
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+ fwrite("\033\001@EJL \n",1,8,prn_stream);
+
+ fflush(prn_stream);
+
+ gs_free(pdev->memory, (char *)buf2, in_size, 1, "lp8000_print_page(buf2)");
+ gs_free(pdev->memory, (char *)buf1, in_size, 1, "lp8000_print_page(buf1)");
+ return 0;
+}