xref: /plan9-contrib/sys/src/cmd/gs/src/gdevimgn.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 /* Copyright (C) 1992, 1993, 1994, 1996 by Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gdevimgn.c,v 1.4 2001/08/01 00:48:23 stefan911 Exp $*/
20 /*
21  * Imagen ImPRESS printer driver - version 1.4
22  *
23  * This driver uses the Impress bitmap operation to print the page image.
24  */
25 
26 /* Written by Alan Millar (AMillar@bolis.sf-bay.org) August 4 1992.
27       Basic bitmap dump.  */
28 /* Updated by Alan Millar Sept 21 1992.  Added resolution handling
29       for 75, 150, and 300 dpi. */
30 /* Updated by Alan Millar June 05 1993.  General cleanup for
31    beta test release. */
32 /* Updated by Alan Millar June 21 1993.  v1.3.  Combined multipage
33    output into single imPress document.  Quote fewer special
34    chars in byte stream mode.  imPress document header options
35    can be set from environment variable IMPRESSHEADER */
36 /* Updated by Alan Millar July 04 1993.  v1.4.
37    New makefile option USE_BYTE_STREAM instead of changing source.
38    Swatch output redone to eliminate ALL blank swatches (swatchMap).
39    Buffer copying changed to multi-byte operations (BIGTYPE).
40    Page margins and A4 paper settings fixed, at least for Canon CX.
41    */
42 
43 /* -------------------------------------------------------- */
44 /* Instructions:
45 
46    - Add "imagen.dev" to DEVICE_DEVS in the makefile.  For example:
47 	DEVICE_DEVS2=laserjet.dev imagen.dev
48 
49    - Include or exclude USE_BYTE_STREAM in makefile as appropriate
50      If you are compiling on Unix, re-run "tar_cat" to update the makefile
51       from devs.mak
52 
53    - At run time, specify the resolution on the GS command line
54       by using -r300 or -r150 or -r75
55    - At run time, specify any imPress document options in
56       the IMPRESSHEADER environment variable.
57    */
58 
59 /* -------------------------------------------------------- */
60 /* Hardware/software combinations tested:
61    - ImageStation IP3 8/300 with parallel byte-stream interface,
62        using GS 2.6.1 on Linux with GCC 2.3.3;
63        earlier using GS 2.5.1 on MS-Dos with Turbo C++ 1.0
64    - Sequenced-packet-protocol interface untested.
65    */
66 /* -------------------------------------------------------- */
67 /* Bugs/Enhancements:
68    - Driver does not use any Impress language features for
69      drawing lines/arcs
70    - Driver does not use resident or downloadable fonts.
71    - Buffer output instead of system call for each byte?
72   */
73 
74 /* -------------------------------------------------------- */
75 #include "gdevprn.h"
76 /* #include <stdio.h> should not be used in drivers */
77 #include <stdlib.h>
78 
79 /* -------------------------------------------------------- */
80 /* Working Constants */
81 
82 /* Byte stream quoting: convert special characters to hex.
83    Specify by including/excluding -DUSE_BYTE_STREAM in makefile.
84    This should match printer's hardware interface configuration.
85    If printer interface is serial with sequenced-packet-protocol
86      spooler software (ImageStation config# 11 = 01), then don't use it.
87      Imagen "ipr" spooler software should not use byte stream.
88    If printer interface is Centronics parallel byte stream,
89      (ImageStation config# 11 = 03), then use byte stream.  */
90 
91 #ifdef USE_BYTE_STREAM
92 #  define BYTE_STREAM 1
93 #else
94 #  define BYTE_STREAM 0
95 #endif
96 
97 /* Byte stream quote character (ImageStation config# 15).
98    Only needed when using byte stream */
99 #define QUOTE_CHAR (char) 0x02
100 /* Byte stream end-of-file character (ImageStation config# 14). */
101 #define EOF_CHAR   (char) 0x04
102 /* Other special characters to quote.  Put them here if spooler or
103    hardware uses flow control, etc.   If not needed, set to
104    a redundant value such as EOF_CHAR */
105 #define EXTRA_QUOTE1 (char) 0x11   /* ^Q */
106 #define EXTRA_QUOTE2 (char) 0x13   /* ^S */
107 #define EXTRA_QUOTE3 EOF_CHAR
108 #define EXTRA_QUOTE4 EOF_CHAR
109 
110 /* -------------------------------------------------------- */
111 /* imPress header default options.
112    Can be overridden at run-time with IMPRESSHEADER env variable */
113 
114 #define IMPRESSHEADER "jobheader onerror, prerasterization off"
115 
116 /* -------------------------------------------------------- */
117 
118 #define CANON_CX
119 
120 /* Printer engine max resolution.  300 for Canon CX models such as
121    ImageStation IP3.  Others (240?) unverified */
122 #ifdef CANON_CX
123 #  define MAX_DPI 300
124 #endif
125 #ifndef MAX_DPI
126 #  define MAX_DPI 300
127 #endif
128 
129 /* Determine imPress scaling factor from GS resolution.
130    Magnify can be 0, 1, or 2.
131     0 = MAX_DPI, 1 = MAX_DPI / 2, 2 = MAX_DPI / 4
132    Assuming MAX_DPI is 300, you can specify -r75 or -r150
133     or -r300 on the GS command line  */
134 #define getMagnification  ( \
135   ( pdev->x_pixels_per_inch > (MAX_DPI >> 1) ) ? 0 : \
136   ( pdev->x_pixels_per_inch > (MAX_DPI >> 2) ) ? 1 : \
137   2 )
138 
139 /* Page dimensions from gdevprn.h - specify -DA4 in makefile for A4 paper */
140 #define WIDTH_10THS   DEFAULT_WIDTH_10THS
141 #define HEIGHT_10THS  DEFAULT_HEIGHT_10THS
142 
143 /* Width in inches of unprintable edge of paper.  May need fine tuning.
144    Canon CX engine in ImageStation IP3 8/300 will only print 8 inches
145    wide on any paper size.  May vary for other engines */
146 
147 #ifdef CANON_CX
148 #  define MARG_L 0.15
149 #  define MARG_R ( (float)WIDTH_10THS / 10.0 - 8.0 - MARG_L)
150 #endif
151 #ifndef MARG_L
152 #  define MARG_L 0.2
153 #endif
154 #ifndef MARG_R
155 #  define MARG_R 0.2
156 #endif
157 #define MARG_T 0.1
158 #define MARG_B 0.2
159 
160 
161 /* Flag for displaying debug messages at run-time.  Higher
162 	number = higher detail */
163 #define IM_DEBUG 0
164 #define DebugMsg(Level,P1,P2) if (Level<=IM_DEBUG) {errprintf(P1,P2 );}
165 
166 /*-------------------------------------------*/
167   /* Impress bitmaps are made up of 32x32 bit swatches.
168      A swatch is four bytes (32 bits) wide by 32 bytes high,
169      totalling 128 bytes.  */
170 #define HorzBytesPerSw 4
171 #define HorzBitsPerSw (HorzBytesPerSw * 8)
172 #define VertBytesPerSw 32
173 #define TotalBytesPerSw (HorzBytesPerSw * VertBytesPerSw)
174 
175 /*-------------------------------------------*/
176 /* Attempt at optimization to something faster than byte-by-byte copying.
177    imPress swatches are 4 bytes wide, so type must align on a 4-byte
178    boundary.  Swatch interleaving restricts the copy to 4 bytes in a row.
179    Type must be numeric where value is zero when all bytes in it are zero. */
180 #if arch_sizeof_long == 4
181 #  define BIGTYPE unsigned long int
182 #else
183 #  if arch_sizeof_short == 4
184 #    define BIGTYPE unsigned short int
185 #  else
186 #    if arch_sizeof_short == 2
187 #      define BIGTYPE unsigned short
188 #    endif
189 #  endif
190 #endif
191 #ifndef BIGTYPE
192 #define BIGTYPE byte
193 #endif
194 
195 #define BIGSIZE ( sizeof( BIGTYPE ) )
196 
197 /*-------------------------------------------*/
198 /*	IMAGEN imPress Command opcodes					*/
199 /* from DVIIMP.C */
200 #define iSP		128	/* advance one space			*/
201 #define	iSP1		129	/* advance one space + 1 pixel		*/
202 #define iMPLUS		131	/* Move one pixel forward		*/
203 #define	iMMINUS		132	/* Move one pixel back			*/
204 #define iMMOVE		133	/* Move in main advance direction	*/
205 #define iSMOVE		134	/* Move in secondary advance direction	*/
206 
207 #define iABS_H		135	/* Move to H position			*/
208 #define iREL_H		136	/* Move in H direction			*/
209 #define iABS_V		137	/* Move to V position			*/
210 #define iREL_V		138	/* Move in V direction			*/
211 
212 #define	iCRLF		197	/* move to beginning of next line	*/
213 
214 #define	iSET_HV_SYSTEM	205	/* Define new coordinate system		*/
215 #define	iSET_ADV_DIRS	206	/* Define advance directions		*/
216 
217 #define	iPAGE		213	/* Set H and V to 0			*/
218 #define iENDPAGE	219	/* print the current page		*/
219 
220 #define iBITMAP		235	/* Print a full bitmap			*/
221 #define iSET_MAGNIFICATION 236
222 				/* magnify the page by 1, 2, 4		*/
223 #define iNOOP		254	/* no operation				*/
224 #define iEOF		255	/* end of impress document		*/
225 
226 /*-------------------------------------------*/
227 /*-------------------------------------------*/
228 /* The device descriptor */
229 
230 private dev_proc_print_page(imagen_print_page);
231 private dev_proc_open_device(imagen_prn_open);
232 private dev_proc_close_device(imagen_prn_close);
233 
234 gx_device_procs imagen_procs =
235 	prn_procs(imagen_prn_open, gdev_prn_output_page, imagen_prn_close);
236 
237 #define ppdev ((gx_device_printer *)pdev)
238 
239 /*-------------------------------------------*/
240 const gx_device_printer far_data gs_imagen_device =
241   prn_device(/*prn_std_procs*/ imagen_procs,
242 	"imagen",
243 	WIDTH_10THS,
244 	HEIGHT_10THS,
245 	MAX_DPI,				/* x_dpi */
246 	MAX_DPI,				/* y_dpi */
247 	MARG_L,MARG_R,MARG_T,MARG_B,		/* margins */
248 	1, imagen_print_page);
249 
250 /*-------------------------------------------*/
251 
252 /*-------------------------------------------*/
253 private void
254 iWrite(FILE *Out, byte Val)
255 { /* iWrite */
256   char *hexList = "0123456789ABCDEF";
257 
258   /* if we are doing byte-stream, quote characters that would otherwise
259      match EOF and QUOTE itself, or other special chars */
260   /* Imagen quoting takes one character and writes out the QUOTE
261      character followed by the hex digits of the quoted character */
262   if (BYTE_STREAM &&
263      (   Val == QUOTE_CHAR   || Val == EOF_CHAR
264       || Val == EXTRA_QUOTE1 || Val == EXTRA_QUOTE2
265       || Val == EXTRA_QUOTE3 || Val == EXTRA_QUOTE4 ) ) {
266     fputc (QUOTE_CHAR, Out);
267     fputc ((char) hexList[Val / 0x10], Out);
268     fputc ((char) hexList[Val % 0x10], Out);
269   } else { /* quoted char */
270     /* Not doing quoting, just send it out */
271     fputc(Val, Out);
272   } /* quoted char */
273 } /* iWrite */
274 
275 /* Write out 16bit, high byte first */
276 void
277 iWrite2(FILE *Out, int Val)
278 { /* iWrite2 */
279   iWrite(Out,(byte) (Val >> 8) & 0x00FF );
280   iWrite(Out,(byte) Val        & 0x00FF );
281 } /* iWrite2 */
282 
283 /* --------------------------------------------------------- */
284 
285 private int
286 imagen_prn_open(gx_device *pdev)
287 { /* imagen_prn_open */
288   int	code;
289 
290   char *impHeader;
291 
292   /* ----------------------------------------- */
293   DebugMsg(1,"%s\n","Start of imagen_prn_open");
294   DebugMsg(2,"BIGSIZE = %ld \n",BIGSIZE);
295 
296   code = gdev_prn_open(pdev);
297   if ( code < 0 ) return code;
298 
299   /* ----------------------------------------- */
300 
301   DebugMsg(2,"opening file: %s\n",ppdev->fname);
302   code = gdev_prn_open_printer(pdev, 1);
303   if ( code < 0 ) return code;
304 
305   impHeader = getenv("IMPRESSHEADER");
306   if (impHeader == NULL ) {
307     impHeader = IMPRESSHEADER ;
308   } /* if impHeader */
309 
310   fprintf(ppdev->file,"@document(language impress, %s)",impHeader);
311 
312   code = gdev_prn_close_printer(pdev);
313   if ( code < 0 ) return code;
314 
315   /* ----------------------------------------- */
316   DebugMsg(1,"%s\n","End of imagen_prn_open");
317 
318   return code;
319 } /* imagen_prn_open */
320 
321 private int
322 imagen_prn_close(gx_device *pdev)
323 { /* imagen_prn_close */
324   int		code;
325 
326   /* ----------------------------------------- */
327   DebugMsg(1,"%s\n","Start of imagen_prn_close");
328 
329   code = gdev_prn_open_printer(pdev, 1);
330   if ( code < 0 ) return code;
331 
332   /* Write imPress end of document marker */
333   iWrite(ppdev->file,iEOF);
334 
335   /* And byte stream end of file */
336   if (BYTE_STREAM) {
337     /* DON'T use iWrite because actual EOF should not be quoted! */
338     fputc(EOF_CHAR,ppdev->file);
339   } /* if byte stream */
340 
341   fflush(ppdev->file);
342 
343   code = gdev_prn_close_printer(pdev);
344   if ( code < 0 ) return code;
345 
346   code = gdev_prn_close(pdev);
347 
348   DebugMsg(1,"%s\n","End of imagen_prn_close");
349 
350   return(code);
351 } /* imagen_prn_close */
352 
353 /*-------------------------------------------*/
354 /* Send the page to the printer. */
355 private int
356 imagen_print_page(gx_device_printer *pdev, FILE *prn_stream)
357 {
358   int line_size = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
359   /* input buffer: one line of bytes rasterized by gs */
360   byte *in = (byte *)gs_malloc(BIGSIZE, line_size / BIGSIZE + 1,
361 	"imagen_print_page(in)");
362   /* output buffer: 32 lines, interleaved into imPress swatches */
363   byte *out;
364   /* working pointer into output buffer */
365   byte *swatch;
366   byte *temp;
367   /* map of which swatches in a row are completely blank, or are non-blank */
368   byte *swatchMap;
369   /* starting line number on page of a row of swatches */
370   int lnum ;
371   /* line number within a row of swatches */
372   int swatchLine;
373   /* ending line number of row of swatches */
374   int lastLine;
375   /* how many swatches can fit on a row */
376   int swatchCount;
377   /* index into row of non-blank swatch */
378   int startSwatch;
379   int endSwatch;
380   /* Scaling factor for resolution */
381   int Magnify;
382   /* page totals */
383   int totalBlankSwatches;
384   int totalGreySwatches;
385 
386   /* ----------------------------------------- */
387   /* Start of routine                          */
388   /* ----------------------------------------- */
389 
390   DebugMsg(1,"%s\n","Start of imagen_print_page");
391 
392   /* ----------------------------------------- */
393   Magnify = getMagnification ;
394 
395   /* Impress bitmaps are made up of 32x32 bit swatches.
396      A swatch is four bytes wide by 32 bytes high.
397      See how many swatches will fit horizontally. */
398 
399   swatchCount = (line_size + HorzBytesPerSw - 1) / HorzBytesPerSw;
400 
401   totalBlankSwatches = 0 ;
402   totalGreySwatches = 0 ;
403   DebugMsg(2,"Swatch count = %d\n",swatchCount);
404   DebugMsg(2,"Line size = %d\n",line_size );
405 
406   out = (byte *)gs_malloc(TotalBytesPerSw , swatchCount + 1,
407 		    "imagen_print_page(out)");
408 
409   swatchMap = (byte *)gs_malloc(BIGSIZE,swatchCount / BIGSIZE + 1,
410 	"imagen_print_page(swatchMap)" );
411 
412   if ( in == 0 || out == 0 )
413     return -1;
414 
415   /* Initialize the page */
416   iWrite(prn_stream,iPAGE);
417 
418   /* Tell ImPress what resolution we will be using */
419   iWrite(prn_stream,iSET_MAGNIFICATION);
420       iWrite(prn_stream,Magnify);
421 
422   /*------------------------------------------------------*/
423   /* main loop down page */
424   lnum = 0;
425   while (lnum <= pdev->height) {
426 
427     /* erase swatch map.  */
428     for (swatch = swatchMap; swatch < swatchMap + swatchCount ;
429 		swatch += BIGSIZE ) {
430       * (BIGTYPE *)swatch = (BIGTYPE) 0;
431     } /* for  */
432 
433     /* get scan lines to fill swatches */
434     swatchLine = 0;
435     lastLine = VertBytesPerSw - 1;
436 
437     /* Check if we don't have a full-height row of swatches at end of page */
438     if (lnum + lastLine > pdev->height ) {
439       /* back up last row so it overlaps with previous.  Not a problem
440 	on a laser printer, because the overlapping part will be identical */
441       lnum = pdev->height - lastLine ;
442     }; /* not full height */
443 
444     DebugMsg (3,"lnum = %d \n",lnum);
445 
446     /* ------------------------------------------------------- */
447     /* get 32 lines and interleave into a row of swatches */
448     for (swatchLine = 0 ; swatchLine <= lastLine; swatchLine++) {
449       /* blank out end of buffer for BIGSIZE overlap */
450       for (temp = in + line_size; temp < in + line_size + BIGSIZE;temp++){
451 	*temp = 0;
452       } /* for temp */
453 
454       /* get one line */
455       gdev_prn_copy_scan_lines(pdev, lnum + swatchLine, in, line_size);
456       DebugMsg(5,"Got scan line %d ", lnum + swatchLine);
457       DebugMsg(5,"line %d \n", swatchLine);
458 
459       /* interleave scan line into swatch buffer */
460       /* a swatch is a 4 byte * 32 byte square.  Swatches are placed
461 	 next to each other.  The first scan line maps into the first
462 	 four bytes of the first swatch, then the first four of the second
463 	 swatch, etc.
464 	 To get this on the page:
465 	   A1  A1  A1  A1  B1  B1  B1  B1  C1  C1  C1  C1
466 	   A2  A2  A2  A2  B2  B2  B2  B2  C2  C2  C2  C2
467 	   ...
468 	   A32 A32 A32 A32 B32 B32 B32 B32 C32 C32 C32 C32
469 	 You have to send it as:
470 	   A1 A1 A1 A1 A2 ... A32 B1 B1 .. B32 C1 C1 ... C32   */
471 
472       /* set initial offset into swatch buffer based on which
473 	 line in the swatch we are processing */
474       swatch = out + swatchLine * HorzBytesPerSw;
475       DebugMsg(5,"offset: swatch = %d \n",(int) (swatch - out) );
476       temp = in;
477       while ( temp < in + line_size ) {
478 	/* copy multi-byte to swatch buffer */
479 	* (BIGTYPE *)swatch = * (BIGTYPE *)temp;
480 	if ( * (BIGTYPE *)temp ) {
481 	  /* mark map if not blank */
482 	  swatchMap[(swatch - out)/TotalBytesPerSw] = (byte) 1 ;
483 	} /* if not zero */
484 
485 	temp   += (BIGSIZE > HorzBytesPerSw) ? HorzBytesPerSw : BIGSIZE ;
486 	swatch += (BIGSIZE > HorzBytesPerSw) ? HorzBytesPerSw : BIGSIZE ;
487 
488 	/* if we copied four bytes, skip to next swatch */
489 	if ( ((temp - in) % HorzBytesPerSw ) == 0 ) {
490 	  swatch += (TotalBytesPerSw - HorzBytesPerSw) ;
491 	} /* if need to skip */
492       } /* while < line_size */
493 
494     } /* for swatchLine */
495 
496     /* ------------------------------------------------- */
497     /* we now have full swatches. */
498     /* Send to printer */
499 
500     /* go through swatch map to find non-blank swatches.
501        Skip over completely blank swatches */
502     startSwatch = 0;
503     while (startSwatch < swatchCount ) {
504       if (swatchMap[startSwatch] == 0 ) {
505 	/* skip blank swatch */
506 	DebugMsg(6,"Skip blank %d \n",startSwatch);
507 	totalBlankSwatches++;
508 	startSwatch++;
509       } else { /* if swatch == 0 */
510 	/* we hit a non-blank swatch. */
511 	totalGreySwatches++;
512 
513 	/* See how many there are in a row */
514 	endSwatch = startSwatch;
515 	while ( (endSwatch < swatchCount) && swatchMap[endSwatch] ) {
516 	  endSwatch++;
517 	  totalGreySwatches++;
518 	} /* while */
519 	/* endSwatch is one past last non-blank swatch */
520 	DebugMsg(6,"Grey swatches %d ",startSwatch);
521 	DebugMsg(6,"until %d \n",endSwatch);
522 
523 	/* vertical position: scan line, shifted for magnification */
524 	iWrite(prn_stream, iABS_V);
525 	  iWrite2(prn_stream, lnum << Magnify);
526 
527 	/* horizontal position = swatch number * 32 bits/swatch */
528 	iWrite(prn_stream,iABS_H);
529 	   iWrite2(prn_stream, startSwatch * HorzBitsPerSw << Magnify );
530 	iWrite(prn_stream,iBITMAP);  /* start bitmap */
531 	  iWrite(prn_stream,0x07);     /* bit OR with page */
532 	  iWrite(prn_stream,(endSwatch - startSwatch)); /* horizontal
533 		number of swatches */
534 	  iWrite(prn_stream, 1) ; /* vertical number of swatches */
535 	/* write out swatch buffer */
536 	for (swatch = out + startSwatch * TotalBytesPerSw;
537 		swatch < out + endSwatch * TotalBytesPerSw; swatch++) {
538 	  iWrite(prn_stream,*swatch);
539 	} /* for swatch */
540 
541 	/* swatches have been printed, see if there are still
542 	   more in this row */
543 	startSwatch = endSwatch;
544       } /* if swatch == 0 */
545 
546     } /* while startSwatch */
547 
548     /* Whole row of swatches is done.  Go on to next row of swatches */
549     lnum += lastLine + 1;
550 
551   } /* while lnum */
552 
553   /* Eject the page */
554   iWrite(prn_stream,iENDPAGE);
555 
556   fflush(prn_stream);
557 
558   gs_free((char *)swatchMap, BIGSIZE, swatchCount / BIGSIZE + 1,
559 	"imagen_print_page(swatchMap)" );
560   gs_free((char *)out, TotalBytesPerSw, swatchCount+1, "imagen_print_page(out)");
561   gs_free((char *)in, BIGSIZE, line_size / BIGSIZE + 1, "imagen_print_page(in)");
562   /* ----------------------------------------- */
563 
564   DebugMsg(1,"Debug: Grey: %d \n",totalGreySwatches);
565   DebugMsg(1,"Debug: Blank: %d \n",totalBlankSwatches );
566   DebugMsg(1,"%s\n","End of imagen_print_page");
567 
568   /* ----------------------------------------- */
569   return 0;
570 
571 } /* imagen_print_page */
572