1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 /* $Id: gdevhl7x.c 9315 2009-01-04 19:58:54Z till $ */
14 /*
15  * Brother HL 720 and 730 driver for Ghostscript
16  *
17  * Note: for the HL 760, use the HP driver.
18  *
19  * The original code was borrowed from the
20  * HP LaserJet/DeskJet driver for Ghostscript.
21  * The code specific to the Brother HL 720 was written by :
22  *       Pierre-Olivier Gaillard (pierre.gaillard@hol.fr)
23  * Thanks to the documentation kindly provided by :
24  *        Richard Thomas <RICHARDT@brother.co.uk>
25  *
26  * Removal of compression code on 1/17/00 by Ross Martin
27  * (ross@ross.interwrx.com, martin@walnut.eas.asu.edu)
28  * enables this driver to correctly print tiger.eps on a
29  * Brother MFC6550MC Fax Machine.  Change to the Horizontal
30  * Offset fixes incorrect page alignment at 300dpi in
31  * Landscape mode with a2ps.
32  */
33 #include "gdevprn.h"
34 /* The following line is used though these printers are not PCL printers*/
35 /* This is because we want the paper size access function */
36 /* (The 720 is a simple GDI printer) */
37 #include "gdevpcl.h"
38 
39 /*
40  * You may select a default resolution of  150 (for 730), 300, or
41  * 600 DPI in the makefile, or an actual resolution on
42  * the gs command line.
43  *
44  * If the preprocessor symbol A4 is defined, the default paper size is
45  * the European A4 size; otherwise it is the U.S. letter size (8.5"x11").
46  *
47  * You may find the following test page useful in determining the exact
48  * margin settings on your printer.  It prints four big arrows which
49  * point exactly to the for corners of an A4 sized paper. Of course the
50  * arrows cannot appear in full on the paper, and they are truncated by
51  * the margins. The margins measured on the testpage must match those
52  * in gdevdjet.c.  So the testpage indicates two facts: 1) the page is
53  * not printed in the right position 2) the page is truncated too much
54  * because the margins are wrong. Setting wrong margins in gdevdjet.c
55  * will also move the page, so both facts should be matched with the
56  * real world.
57 
58 %!
59 	newpath
60 	0 0 moveto 144 72 lineto 72 144 lineto
61 	closepath fill stroke 0 0 moveto 144 144 lineto stroke
62 
63 	595.27 841.88 moveto 451.27 769.88 lineto 523.27 697.88 lineto
64 	closepath fill stroke 595.27 841.88 moveto 451.27 697.88 lineto stroke
65 
66 	0 841.88 moveto 144 769.88 lineto 72 697.88 lineto
67 	closepath fill stroke 0 841.88 moveto 144 697.88 lineto stroke
68 
69 	595.27 0 moveto 451.27 72 lineto 523.27 144 lineto
70 	closepath fill stroke 595.27 0 moveto 451.27 144 lineto stroke
71 
72 	/Helvetica findfont
73 	14 scalefont setfont
74 	100 600 moveto
75 	(This is an A4 testpage. The arrows should point exactly to the) show
76 	100 580 moveto
77 	(corners and the margins should match those given in gdev*.c) show
78 	showpage
79 
80  */
81 
82 #define USE_POSSIBLY_FLAWED_COMPRESSION 1
83 
84 /* Type definitions */
85 typedef struct {
86   short width;                /* physical width of the paper */
87   short height;               /* physical height of the paper */
88 }                 PaperFormat; /* Rep. of the charateristics of a sheet of paper */
89 
90 typedef unsigned char Byte; /* Rep. of elementary data unit */
91 
92 
93 
94 /*
95  * Definition of a Helper structure to handle a list of commands
96  */
97 typedef struct {
98   Byte * data;
99   short maxSize;
100   short current;
101 
102 } ByteList;
103 
104 /*
105  * Type for representing a summary of the previous lines
106  *
107  */
108 
109 typedef struct {
110   short  previousSize;
111   Byte   previousData[1500]; /* Size bigger than any possible line */
112   short  nbBlankLines;
113   short  nbLinesSent;
114   short  pageWidth;
115   short  pageHeight;
116   short  horizontalOffset;
117   short  resolution;
118 } Summary;
119 
120 
121 
122 /* Constants */
123 
124 /* We need a boolean : true , we got it from gdevprn.h */
125 
126 /* Other constants */
127 static const int DumpFinished = 0;
128 static const int DumpContinue = 1;
129 static const int HL7X0_LENGTH = 5; /* Length of a command to tell the size of the data to be sent to the printer*/
130 static void  makeCommandsForSequence(Byte     * pSource,
131 				      short      length,
132 				      ByteList * pCommandList,
133 				      short      offset,
134 				      Byte     * pCommandCount,
135 				      short      rest);
136 
137 /* Auxiliary Functions */
138 
139 
140 
141 static int dumpPage(gx_device_printer * pSource,
142 		      Byte              * pLineTmp,
143 		      ByteList          * pCommandList,
144 		      Summary           * pSummary
145 		      );
146 static void initSummary(Summary * s,short pw, short ph, short resolution);
147 
148 static void resetPreviousData(Summary * s);
149 
150 static void makeFullLine( Byte      * pCurrentLine,
151 			   Byte      * pPreviousLine,
152 			   short       lineWidth,
153 			   ByteList  * commandsList,
154 			   short       horizontalOffset
155 			   );
156 
157 
158 
159 /*
160  * Initialize a list of Bytes structure
161  */
162 static void initByteList(ByteList *list, Byte *array, short maxSize,short initCurrent);
163 static void addByte(ByteList *list,Byte value );
164 static void addArray(ByteList *list, Byte *source, short nb);
165 static void addNBytes(ByteList * list, Byte value, short nb);
166 static Byte * currentPosition(ByteList * list);
167 static void addCodedNumber(ByteList * list, short number);
168 static int isThereEnoughRoom(ByteList * list, short biggest);
169 static short roomLeft(ByteList * list);
170 static void dumpToPrinter(ByteList * list,FILE * printStream);
171 
172 /* Real Print function */
173 
174 static int hl7x0_print_page(gx_device_printer *, FILE *, int, int, ByteList *);
175 
176 
177 
178 
179 
180 
181 /* Define the default, maximum resolutions. */
182 #ifdef X_DPI
183 #  define X_DPI2 X_DPI
184 #else
185 #  define X_DPI 300
186 #  define X_DPI2 600
187 #endif
188 #ifdef Y_DPI
189 #  define Y_DPI2 Y_DPI
190 #else
191 #  define Y_DPI 300
192 #  define Y_DPI2 600
193 #endif
194 
195 
196 #define LETTER_WIDTH 5100
197 #define LEFT_MARGIN  30
198 /* The following table is not actually used.... */
199 static const PaperFormat tableOfFormats[] = {
200     /*  0 P LETTER */ { 2550, 3300 },
201     /*  1 P LEGAL  */ { 2550, 4200 },
202     /*  2 P EXEC   */ { 2175, 3150 },
203     /*  3 P A4(78) */ { 2480, 3507 },
204     /*  4 P B5     */ { 2078, 2953 },
205     /*  5 P A5     */ { 1754, 2480 },
206     /*  6 P MONARC */ { 1162, 2250 },
207     /*  7 P COM10  */ { 1237, 2850 },
208     /*  8 P DL     */ { 1299, 2598 },
209     /*  9 P C5     */ { 1913, 2704 },
210     /* 10 P A4Long */ { 2480, 4783 },
211 
212     /* 11 L LETTER */ { 3300, 2550 },
213     /* 12 L LEGAL  */ { 4200, 2550 },
214     /* 13 L EXEC   */ { 3150, 2175 },
215     /* 14 L A4     */ { 3507, 2480 },
216     /* 15 L B5     */ { 2952, 2078 },
217     /* 16 L A5     */ { 2480, 1754 },
218     /* 17 L MONARC */ { 2250, 1162 },
219     /* 18 L COM10  */ { 2850, 1237 },
220     /* 19 L DL     */ { 2598, 1299 },
221     /* 20 L C5     */ { 2704, 1913 },
222     /* 21 L A4Long */ { 4783, 2480 }
223 };
224 
225 
226 /* Compute the maximum length of a compressed line */
MaxLineLength(short resolution)227 static short MaxLineLength(short resolution){
228 return (((156 * resolution / 150 ) * 5 )/4) + 8;
229 }
230 
231 
232 /* Margins are left, bottom, right, top. */
233 /* Quotation from original gdevdjet.c */
234 /* from Frans van Hoesel hoesel@rugr86.rug.nl.  */
235 /* A4 has a left margin of 1/8 inch and at a printing width of
236  * 8 inch this give a right margin of 0.143. The 0.09 top margin is
237  * not the actual margin - which is 0.07 - but compensates for the
238  * inexact paperlength which is set to 117 10ths.
239  * Somebody should check for letter sized paper. I left it at 0.07".
240  */
241 
242 
243 /* The A4 margins are almost good */
244 /* The one for Letter are those of the gdevdjet.c file... */
245 #define HL7X0_MARGINS_A4	0.1, 0.15, 0.07, 0.05
246 #define HL7X0_MARGINS_LETTER 0.275, 0.20, 0.25, 0.07
247 
248 
249 
250 /* We round up the LINE_SIZE to a multiple of a ulong for faster scanning. */
251 #define W sizeof(word)
252 
253 /* Printer types */
254 
255 #define HL720    0
256 #define HL730    0 /* No difference */
257 
258 
259 
260 
261 /* The device descriptors */
262 static dev_proc_open_device(hl7x0_open);
263 static dev_proc_close_device(hl7x0_close);
264 static dev_proc_print_page(hl720_print_page);
265 static dev_proc_print_page(hl730_print_page);
266 
267 
268 
269 static const gx_device_procs prn_hl_procs =
270   prn_params_procs(hl7x0_open, gdev_prn_output_page, hl7x0_close,
271 		   gdev_prn_get_params, gdev_prn_put_params);
272 
273 
274 const gx_device_printer far_data gs_hl7x0_device =
275   prn_device(prn_hl_procs, "hl7x0",
276 	DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
277 	X_DPI, Y_DPI,
278 	0, 0, 0, 0,		/* margins filled in by hl7x0_open */
279 	1, hl720_print_page); /* The hl720 and hl730 can both use the same print method */
280 
281 
282 
283 /* Open the printer, adjusting the margins if necessary. */
284 
285 static int
hl7x0_open(gx_device * pdev)286 hl7x0_open(gx_device *pdev)
287 {	/* Change the margins if necessary. */
288 	static const float m_a4[4] = { HL7X0_MARGINS_A4 };
289 	static const float m_letter[4] = { HL7X0_MARGINS_LETTER };
290 	const float *m =
291 	  (gdev_pcl_paper_size(pdev) == PAPER_SIZE_A4 ? m_a4 : m_letter);
292 
293 	gx_device_set_margins(pdev, m, true);
294 	return gdev_prn_open(pdev);
295 }
296 
297 
298 /* The orders sent are those provided in the Brother DOS example */
299 static int
hl7x0_close(gx_device * pdev)300 hl7x0_close(gx_device *pdev)
301 {
302     gx_device_printer *const ppdev = (gx_device_printer *)pdev;
303     int code = gdev_prn_open_printer(pdev, 1);
304 
305     if (code < 0)
306 	return code;
307     fputs("@N@N@N@N@X", ppdev->file) ;
308     return gdev_prn_close_printer(pdev);
309 }
310 
311 /* ------ Internal routines ------ */
312 
313 /* The HL 720 can compress*/
314 static int
hl720_print_page(gx_device_printer * pdev,FILE * prn_stream)315 hl720_print_page(gx_device_printer *pdev, FILE *prn_stream)
316 {
317 	Byte prefix[] ={
318    0x1B,'%','-','1','2','3','4','5','X'
319   ,'@','P','J','L',0x0A                         /* set PJL mode */
320   ,'@','P','J','L',' ','E','N','T','E','R',' '
321   ,'L','A','N','G','U','A','G','E'
322   ,' ','=',' ','H','B','P',0x0A                 /* set GDI Printer mode */
323   ,'@','L', 0x0
324    };
325 	ByteList initCommand;
326     	int x_dpi = pdev->x_pixels_per_inch;
327 	initByteList(&initCommand,
328 		     prefix,         /* Array */
329 		     sizeof(prefix), /* Total size */
330 		     sizeof(prefix) - 1); /* Leave one byte free since*/
331 	/* we need to add the following order at the end */
332 	addByte(&initCommand, (Byte) ((((600/x_dpi) >> 1) \
333 						  | (((600/x_dpi) >> 1) << 2))));
334 	/* Put the value of the used resolution into the init string */
335 
336 	return hl7x0_print_page(pdev, prn_stream, HL720, 300,
337 	       &initCommand);
338 }
339 /* The HL 730 can compress  */
340 static int
hl730_print_page(gx_device_printer * pdev,FILE * prn_stream)341 hl730_print_page(gx_device_printer *pdev, FILE *prn_stream)
342 {	return hl720_print_page(pdev, prn_stream);
343 }
344 
345 /* Send the page to the printer.  For speed, compress each scan line, */
346 /* since computer-to-printer communication time is often a bottleneck. */
347 static int
hl7x0_print_page(gx_device_printer * pdev,FILE * printStream,int ptype,int dots_per_inch,ByteList * initCommand)348 hl7x0_print_page(gx_device_printer *pdev, FILE *printStream, int ptype,
349   int dots_per_inch, ByteList *initCommand)
350 {
351 	/* UTILE*/
352   /* Command for a formFeed (we can't use strings because of the zeroes...)*/
353   Byte FormFeed[] = {'@','G',0x00,0x00,0x01,0xFF,'@','F'};
354   ByteList formFeedCommand;
355   /* Main characteristics of the page */
356   int line_size       = gdev_mem_bytes_per_scan_line((gx_device *)pdev);
357   int x_dpi = pdev->x_pixels_per_inch;
358   /*  int y_dpi = pdev->y_pixels_per_inch; */
359   int num_rows = dev_print_scan_lines(pdev);
360   int result;
361   int sizeOfBuffer   = MaxLineLength(x_dpi) + 30;
362   Byte * storage      = (Byte *) gs_malloc(pdev->memory,
363 					   sizeOfBuffer + line_size,
364 					   1,
365 					   "hl7x0_print_page");
366 	/*	bool dup = pdev->Duplex; */
367 	/* bool dupset = pdev->Duplex_set >= 0; */
368 	Summary pageSummary;
369 	ByteList commandsBuffer;
370 	initSummary(&pageSummary,
371 		    line_size,
372 		    num_rows,
373 		    x_dpi);
374 	if ( storage == 0 )	/* can't allocate working area */
375 		return_error(gs_error_VMerror);
376 	initByteList(&commandsBuffer, storage, sizeOfBuffer,0 );
377 	/* PLUS A MOI */
378 	if ( pdev->PageCount == 0 )
379 	  {
380 	    /* Put out init string before first page. */
381 	    dumpToPrinter(initCommand, printStream);		/* send init to printer */
382 
383 	}
384 
385 	do {
386 	  result = dumpPage(pdev,
387 			    storage + sizeOfBuffer, /* The line buffer is after the dump buffer */
388 			    &commandsBuffer,
389 			    &pageSummary);
390 	  dumpToPrinter(&commandsBuffer,printStream);
391 
392 	} while (result == DumpContinue);
393 
394 
395 	/* end raster graphics and eject page */
396 	initByteList(&formFeedCommand,
397 		     FormFeed,          /* Array */
398 		     sizeof(FormFeed),  /* Size in bytes */
399 		     sizeof(FormFeed)); /* First free byte */
400 	dumpToPrinter(&formFeedCommand, printStream);
401 
402 	/* free temporary storage */
403 	gs_free(pdev->memory, (char *)storage, storage_size_words, 1, "hl7X0_print_page");
404 
405 	return 0; /* If we reach this line, it means there was no error */
406 }
407 
408 /*
409  * Useful auxiliary declarations
410  *
411  */
412 
413 
stripTrailingBlanks(Byte * line,short length)414 static short stripTrailingBlanks(Byte * line, short length){
415   short positionOfFirstZero = length - 1;
416   while (positionOfFirstZero > 0) {
417     if (line[positionOfFirstZero] != 0) {
418       return positionOfFirstZero + 1;
419     }
420     positionOfFirstZero -- ;
421   }
422   return 0;
423 }
424 
425 /*
426  * Changed the horizontalOffset function 1/17/00 Ross Martin.
427  * ross@ross.interwrx.com or martin@walnut.eas.asu.edu
428  *
429  * The equation used to muliply pixWidth by resolution/600
430  * also.  This didn't work right at resolution 300; it caused
431  * landscape pages produced by a2ps to be half off the
432  * page, when they were not at 600dpi or on other
433  * devices.  I'm not sure the equation below is exactly
434  * correct, but it now looks to be pretty close visually,
435  * and works correctly at 600dpi and 300dpi.
436  */
horizontalOffset(short pixWidth,short pixOffset,short resolution)437 static short horizontalOffset(short pixWidth,
438 			      short pixOffset,
439 			      short resolution){
440 return (((LETTER_WIDTH * resolution/600 - pixWidth) + pixOffset * 2) + 7) / 8;
441 
442 }
443 
444 
445 
446 /*
447  * First values in a Summary
448  */
initSummary(Summary * s,short pw,short ph,short resolution)449 static void initSummary(Summary * s,short pw, short ph, short resolution){
450   s->previousSize = -1 ;
451   s->nbBlankLines = 1;
452   s->nbLinesSent = 0;
453   s->pageWidth = pw; /* In Bytes */
454   s->pageHeight = ph;
455   s->horizontalOffset = horizontalOffset( pw * 8,LEFT_MARGIN, resolution) ;
456   s->resolution = resolution;
457 }
458 
459 /*
460  * The previous line was blank, so we need to clean the corresponding array
461  */
resetPreviousData(Summary * s)462 static void resetPreviousData(Summary * s){
463  memset(s->previousData,0,s->pageWidth);
464 }
465 
466 
467 /*
468  * dumpPage :
469  *
470  */
dumpPage(gx_device_printer * pSource,Byte * pLineTmp,ByteList * pCommandList,Summary * pSummary)471 static int dumpPage(gx_device_printer * pSource,
472 		      Byte              * pLineTmp,
473 		      ByteList          * pCommandList,
474 		      Summary           * pSummary
475 		      ){
476 
477   /* Declarations */
478   Byte * pSaveCommandStart;
479   short  lineNB;
480   short usefulLength;
481   short tmpLength;
482   /* Initializations */
483   /* Make room for size of commands buffer */
484   pSaveCommandStart = currentPosition(pCommandList);
485   addNBytes(pCommandList,0,HL7X0_LENGTH);
486   /* pSource += pSummary->nbLinesSent * pSummary->pageWidth;*/
487   /* Process all possible Lines */
488   for (lineNB = pSummary->nbLinesSent /*ERROR? + nbBlankLines */ ;
489        lineNB < pSummary->pageHeight ; lineNB ++ ) {
490     /* Fetch the line and put it into the buffer */
491     gdev_prn_copy_scan_lines(pSource,
492 			     lineNB,
493 			     pLineTmp,
494 			     pSummary->pageWidth);
495 
496     usefulLength =  stripTrailingBlanks(pLineTmp,pSummary->pageWidth);
497     if (usefulLength != 0) {
498 
499       /* The line is not blank */
500       /* Get rid of the precedent blank lines */
501       if (pSummary->nbBlankLines != 0) {
502 	if ( isThereEnoughRoom( pCommandList, pSummary->nbBlankLines )   ) {
503 
504 	  addNBytes(pCommandList,0xff,pSummary->nbBlankLines);
505 	  pSummary->nbBlankLines = 0;
506 
507 	}
508 	else {
509 
510 	  short availableRoom = roomLeft(pCommandList);
511 	  addNBytes(pCommandList,0xff,availableRoom);
512 	  pSummary->nbBlankLines -= availableRoom;
513 
514 	  break ; /* We have no more room */
515 
516 	}
517 
518 	resetPreviousData(pSummary); /* Make sure there are zeroes for the previous line */
519 	pSummary->previousSize = 0; /* The previous line was empty */
520 
521       }
522 
523       /* Deal with the current line */
524       if (!isThereEnoughRoom(pCommandList,MaxLineLength(pSummary->resolution))){
525 	break; /* We can process this line */
526       }
527 
528       if (pSummary->previousSize > usefulLength){
529 	tmpLength = pSummary->previousSize;
530       }
531       else {
532 	tmpLength = usefulLength;
533       }
534 
535       if (pSummary->previousSize == -1 ) {/* This is the first line */
536 
537 	Byte *save = currentPosition(pCommandList);
538 	addByte(pCommandList,0); /* One byte for the number of commands */
539 
540 	makeCommandsForSequence(pLineTmp,
541 				tmpLength,
542 				pCommandList,
543 				pSummary->horizontalOffset,
544 				save,
545 				0);
546       }
547       else { /*There is a previous line */
548 
549 	makeFullLine(pLineTmp,
550 		     pSummary->previousData,
551 		     tmpLength,
552 		     pCommandList,
553 		     pSummary->horizontalOffset);
554       }
555       /* The present line will soon be considered as "previous" */
556       pSummary->previousSize = tmpLength;
557       /* Update the data representing the line will soon be the "previous line" */
558       memcpy(pSummary->previousData,pLineTmp,tmpLength);
559 
560     }
561     else { /* the current line is blank */
562       pSummary->nbBlankLines++;
563     }
564 
565   /* And one more line */
566     pSummary->nbLinesSent ++;
567   }
568 
569   if (pCommandList->current > HL7X0_LENGTH){
570     short size = pCommandList->current - HL7X0_LENGTH;
571     *(pSaveCommandStart++)  = '@';
572     *(pSaveCommandStart++)  = 'G';
573     *(pSaveCommandStart++)  = (Byte) (size >> 16);
574     *(pSaveCommandStart++)  = (Byte) (size >> 8);
575     *(pSaveCommandStart++)  = (Byte) (size);
576   }
577   else {  /* We only met blank lines and reached the end of the page */
578     pCommandList->current = 0;
579   }
580   if (lineNB == pSummary->pageHeight){
581     return DumpFinished;
582   }
583   else {
584     return DumpContinue;
585   }
586 }
587 
588 
589 /*
590  *  makeFullLine :
591  *  process an arbitrary line for which a former line is available
592  *  The line will be split in sequences that are different from the
593  * corresponding ones of the previous line. These sequences will be processed
594  * by makeCommandsOfSequence.
595  */
596 
597 
598 
makeFullLine(Byte * pCurrentLine,Byte * pPreviousLine,short lineWidth,ByteList * commandsList,short horizontalOffset)599 static void makeFullLine( Byte      * pCurrentLine,
600 			   Byte      * pPreviousLine,
601 			   short       lineWidth,
602 			   ByteList  * commandsList,
603 			   short       horizontalOffset
604 			   ){
605   /* Declarations */
606   Byte *pPreviousTmp;
607   Byte *pCurrentTmp;
608   Byte *pNumberOfCommands;
609   int loopCounter;
610   short remainingWidth;
611   Byte *pStartOfSequence;
612   /*****************/
613   /* Special cases */
614   /*****************/
615 
616   /* I believe this situation to be impossible */
617   if (lineWidth <= 0) {
618     addByte(commandsList,0xff);
619     return;
620   }
621 
622   /*******************/
623   /* Initializations */
624   /*******************/
625 
626   pNumberOfCommands = currentPosition(commandsList); /* Keep a pointer to the number of commands */
627   addByte(commandsList,0); /* At the moment there are 0 commands */
628 
629   pPreviousTmp = pPreviousLine;
630   pCurrentTmp = pCurrentLine;
631 
632   /* Build vector of differences with a Xor */
633 
634   for (loopCounter = lineWidth ;  0 < loopCounter ; loopCounter -- )
635     *pPreviousTmp++ ^= *pCurrentTmp++;
636 
637   /* Find sequences that are different from the corresponding (i.e. vertically aligned)
638    * one of the previous line. Make commands for them.
639    */
640 
641   pStartOfSequence = pPreviousLine;
642   remainingWidth = lineWidth;
643 
644   while (true) {
645 
646     /*
647      * Disabled line-to-line compression, 1/17/00 Ross Martin
648      * ross@ross.interwrx.com and/or martin@walnut.eas.asu.edu
649      *
650      * The compression here causes problems printing tiger.eps.
651      * The problem is vertical streaks.  The printer I'm printing
652      * to is a Brother MFC6550MC Fax Machine, which may be
653      * slightly different from the hl720 and hl730.  Note that
654      * this fax machine does support HP LaserJet 2p emulation,
655      * but in order to enable it I believe one needs special
656      * setup from a DOS program included with the printer.  Thus,
657      * the hl7x0 driver seems a better choice.  In any case,
658      * on the MFC6550MC, some files print fine with compression
659      * turned on, but others such as tiger.eps print with streaks.
660      * disabling the compression fixes the problem, so I haven't
661      * looked any further at the cause.  It may be that the
662      * compression is correct for the hl720 and hl730, and only
663      * different for the MFC6550MC, or it may be that tiger.eps
664      * won't print correctly with compression enabled on any
665      * of these.  It may be that the problem is only with color
666      * and/or grayscale prints.  YMMV.  I don't think it likely
667      * that turning off compression will cause problems with
668      * other printers, except that they may possibly print slower.
669      */
670 
671 #ifdef USE_POSSIBLY_FLAWED_COMPRESSION
672     /* Count and skip bytes that are not "new" */
673     while (true) {
674       if (remainingWidth == 0)  /* There is nothing left to do */
675 	{
676 	  return;
677 	}
678       if (*pStartOfSequence != 0)
679 	break;
680       pStartOfSequence ++;
681       horizontalOffset ++; /* the offset takes count of the bytes that are not "new" */
682       --remainingWidth;
683     }
684 #endif
685 
686     pPreviousTmp = pStartOfSequence + 1; /* The sequence contains at least this byte */
687     --remainingWidth;
688 
689     /* Find the end of the sequence of "new" bytes */
690 
691 #ifdef USE_POSSIBLY_FLAWED_COMPRESSION
692     while (remainingWidth != 0 && *pPreviousTmp != 0) {
693       ++pPreviousTmp; /* Enlarge the sequence Of new bytes */
694       --remainingWidth;
695     }
696 #else
697    pPreviousTmp += remainingWidth;
698    remainingWidth = 0;
699 #endif
700 
701     makeCommandsForSequence(pCurrentLine + (pStartOfSequence - pPreviousLine),
702 			     pPreviousTmp - pStartOfSequence,
703 			     commandsList,
704 			     horizontalOffset,
705 			     pNumberOfCommands,
706 			     remainingWidth);
707     if (*pNumberOfCommands == 0xfe   /* If the number of commands has reached the maximum value */
708 	||                           /* or */
709 	remainingWidth == 0 )        /* There is nothing left to process */
710     {
711       return;
712     }
713 
714     pStartOfSequence = pPreviousTmp + 1; /* We go on right after the sequence of "new" bytes */
715     horizontalOffset = 1;
716     --remainingWidth;
717   } /* End of While */
718 
719 
720 
721 
722 } /* End of makeFullLine */
723 
724 
725 
726 /*
727  *  Declarations of functions that are defined further in the file
728  */
729 static void makeSequenceWithoutRepeat(
730 				  Byte     * pSequence,
731 				  short      lengthOfSequence,
732 				  ByteList * pCommandList,
733 				  short      offset             );
734 
735 static void makeSequenceWithRepeat(
736 				  Byte     * pSequence,
737 				  short      lengthOfSequence,
738 				  ByteList * pCommandList,
739 				  short      offset             );
740 
741 
742 /*
743  * makeCommandsForSequence :
744  * Process a sequence of new bytes (i.e. different from the ones on the former line)
745  */
746 
makeCommandsForSequence(Byte * pSource,short length,ByteList * pCommandList,short offset,Byte * pNumberOfCommands,short rest)747 static void makeCommandsForSequence(Byte     * pSource,
748 				     short      length,
749 				     ByteList * pCommandList,
750 				     short      offset,
751 				     Byte     * pNumberOfCommands,
752 				     short      rest)         {
753   /* Declarations */
754   Byte * pStartOfSequence;
755   Byte * pEndOfSequence;
756   short  remainingLength = length - 1;
757 
758   pStartOfSequence = pSource;
759   pEndOfSequence = pStartOfSequence + 1;
760   /*
761    * Process the whole "new" Sequence that is divided into
762    * repetitive and non-repetitive sequences.
763    */
764   while (true) {
765 
766     /* If we have already stored too many commands, make one last command with
767      * everything that is left in the line and return.
768      */
769     if (*pNumberOfCommands == 0xfd) {
770       makeSequenceWithoutRepeat(pStartOfSequence,
771 			1 + remainingLength + rest,
772 			pCommandList,
773 			offset);
774       ++*pNumberOfCommands;
775       return;
776     }
777 
778     /* Start with a sub-sequence without byte-repetition */
779     while (true) {
780       /* If we have completed the last subsequence */
781       if (remainingLength == 0) {
782 	makeSequenceWithoutRepeat(pStartOfSequence,
783 		     pEndOfSequence - pStartOfSequence,
784 		     pCommandList,
785 		     offset);
786 	++*pNumberOfCommands;
787 	return;
788       }
789       /* If we have discovered a repetition */
790       if (*pEndOfSequence == *(pEndOfSequence - 1)) {
791 	break;
792       }
793       ++ pEndOfSequence; /* The subsequence is bigger*/
794       --remainingLength;
795     }
796     /* If this is a sequence without repetition */
797     if (pStartOfSequence != pEndOfSequence - 1) {
798       makeSequenceWithoutRepeat(pStartOfSequence,
799 				(pEndOfSequence - 1) - pStartOfSequence,
800 				pCommandList,
801 				offset);
802       ++*pNumberOfCommands;
803       offset = 0;
804       pStartOfSequence = pEndOfSequence - 1;
805 
806       /* If we have too many commands */
807       if (*pNumberOfCommands == 0xfd) {
808 	makeSequenceWithoutRepeat(pStartOfSequence,
809 				  1 + remainingLength + rest,
810 				  pCommandList,
811 				  offset);
812 	++*pNumberOfCommands;
813 	return;
814       }
815     } /* End If */
816 
817     /*
818      * Process a subsequence that repeats the same byte
819      */
820     while (true) {
821       /* If there is nothing left to process */
822       if (remainingLength == 0) {
823 	makeSequenceWithRepeat(pStartOfSequence,
824 			       pEndOfSequence - pStartOfSequence,
825 			       pCommandList,
826 			       offset);
827 	++*pNumberOfCommands;
828 	return;
829       }
830       /* If we find a different byte */
831       if (*pEndOfSequence != *pStartOfSequence){
832 	break;
833       }
834       ++pEndOfSequence; /* The subsequence is yet bigger */
835       --remainingLength;
836     } /* End of While */
837       makeSequenceWithRepeat(pStartOfSequence,
838 			     pEndOfSequence - pStartOfSequence,
839 			     pCommandList,
840 			     offset);
841       ++*pNumberOfCommands;
842       offset = 0;   /* The relative offset between two subsequences is 0 */
843       pStartOfSequence = pEndOfSequence ++ ; /* we loop again from the end of this subsequence */
844       --remainingLength;
845 
846   } /* End of While */
847 
848 } /* End makeCommandsForSequence */
849 
850 
851 
852 
853 
854 
855 
856 
857 /*
858  * makeSequenceWithoutRepeat
859  */
makeSequenceWithoutRepeat(Byte * pSequence,short lengthOfSequence,ByteList * pCommandList,short offset)860 static void makeSequenceWithoutRepeat(
861 				  Byte     * pSequence,
862 				  short      lengthOfSequence,
863 				  ByteList * pCommandList,
864 				  short      offset             ){
865   /*
866    *   Constant definitions
867    */
868   static const short MAX_OFFSET         = 15;
869   static const short POSITION_OF_OFFSET = 3;
870   static const short MAX_LENGTH         =  7;
871 
872   Byte tmpFirstByte = 0;
873   Byte * pSaveFirstByte;
874   short reducedLength = lengthOfSequence - 1; /* Length is alway higher than 1
875 						 Therefore a reduced value is stored
876 						 */
877   /* Initialization */
878 
879   pSaveFirstByte = currentPosition(pCommandList);
880   addByte( pCommandList, 0 /* Dummy value */);
881 
882   /* Computations */
883 
884   if (offset >= MAX_OFFSET) {
885     addCodedNumber(pCommandList,offset - MAX_OFFSET);
886     tmpFirstByte |= MAX_OFFSET << POSITION_OF_OFFSET;
887   }
888   else
889     tmpFirstByte |= offset << POSITION_OF_OFFSET;
890 
891   if (reducedLength >= MAX_LENGTH) {
892     addCodedNumber(pCommandList,reducedLength - MAX_LENGTH);
893     tmpFirstByte |= MAX_LENGTH ;
894   }
895   else
896     tmpFirstByte |= reducedLength ;
897   /* Add a copy of the source sequence */
898 
899   addArray(pCommandList, pSequence, lengthOfSequence);
900 
901   /* Store the computed value of the first byte */
902 
903   *pSaveFirstByte = tmpFirstByte;
904 
905   return ;
906 } /* End of makeSequenceWithoutRepeat */
907 
908 
909 
910 /*
911  * makeSequenceWithRepeat
912  */
makeSequenceWithRepeat(Byte * pSequence,short lengthOfSequence,ByteList * pCommandList,short offset)913 static void makeSequenceWithRepeat(
914 				  Byte     * pSequence,
915 				  short      lengthOfSequence,
916 				  ByteList * pCommandList,
917 				  short      offset             ){
918   /*
919    *   Constant definitions
920    */
921   static const short MAX_OFFSET         = 3;
922   static const short POSITION_OF_OFFSET = 5;
923   static const short MAX_LENGTH         =  31;
924 
925   Byte tmpFirstByte = 0x80;
926   Byte * pSaveFirstByte;
927   short reducedLength = lengthOfSequence - 2; /* Length is always higher than 2
928 						 Therefore a reduced value is stored
929 						 */
930   /* Initialization */
931 
932   pSaveFirstByte = currentPosition(pCommandList);
933   addByte( pCommandList, 0 /* Dummy value */);
934 
935   /* Computations */
936 
937   if (offset >= MAX_OFFSET) {
938     addCodedNumber(pCommandList, offset - MAX_OFFSET);
939     tmpFirstByte |= MAX_OFFSET << POSITION_OF_OFFSET;
940   }
941   else
942     tmpFirstByte |= offset << POSITION_OF_OFFSET;
943 
944   if (reducedLength >= MAX_LENGTH) {
945     addCodedNumber(pCommandList,reducedLength - MAX_LENGTH);
946     tmpFirstByte |= MAX_LENGTH ;
947   }
948   else
949     tmpFirstByte |= reducedLength ;
950   /* Add a copy the byte that is repeated throughout the sequence */
951 
952   addByte(pCommandList, *pSequence );
953 
954   /* Store the computed value of the first byte */
955 
956   *pSaveFirstByte = tmpFirstByte;
957 
958   return ;
959 } /* End of makeSequenceWithRepeat*/
960 
961 
962 
963 
964 /*
965  * Initialize a list of Bytes structure
966  */
initByteList(ByteList * list,Byte * array,short maxSize,short initCurrent)967 static void initByteList(ByteList *list, Byte *array, short maxSize, short initCurrent) {
968   list->current = initCurrent;
969   list->maxSize = maxSize;
970   list->data = array;
971 }
972 
973 /*
974  * Add a Byte to a list of Bytes
975  */
addByte(ByteList * list,Byte value)976 static void addByte(ByteList *list,Byte value ) {
977  if (list->current < list->maxSize)
978   list->data[list->current++] = value;
979  else
980    errprintf("Could not add byte to command\n");
981 }
982 
983 
984 /*
985  * Add a copy of an array to a list of Bytes
986  */
987 
addArray(ByteList * list,Byte * source,short nb)988 static void addArray(ByteList *list, Byte *source, short nb){
989   if (list->current <= list->maxSize - nb)
990   {
991     memcpy(list->data + list->current, source , (size_t) nb);
992     list->current += nb;
993   }
994   else
995     errprintf("Could not add byte array to command\n");
996 }
997 
998 
999 /*
1000  * Add N bytes to a list of Bytes
1001  */
1002 
addNBytes(ByteList * list,Byte value,short nb)1003 static void addNBytes(ByteList * list, Byte value, short nb){
1004   int i;
1005   if (list->current <= list->maxSize - nb)
1006   {
1007     for (i = list->current ; i < (list->current + nb) ; i++)
1008       {
1009 	list->data[i] = value;
1010       }
1011     list->current += nb;
1012   }
1013   else
1014     errprintf("Could not add %d bytes to command\n",nb);
1015 }
1016 
1017 /*
1018  * Get pointer to the current byte
1019  */
currentPosition(ByteList * list)1020 static Byte * currentPosition(ByteList * list) {
1021   return &(list->data[list->current]);
1022 }
1023 
1024 /*
1025  * add a number coded in the following way :
1026  * q bytes with 0xff value
1027  * 1 byte with r value
1028  * where q is the quotient of the number divided by 0xff and r is the
1029  * remainder.
1030  */
addCodedNumber(ByteList * list,short number)1031 static void addCodedNumber(ByteList * list, short number){
1032  short q = number / 0xff;
1033  short r = number % 0xff;
1034 
1035  addNBytes(list, 0xff, q);
1036  addByte(list,r);
1037 
1038 }
1039 
1040 /*
1041  * See if there is enough room for a set of commands of size biggest
1042  *
1043  */
1044 
isThereEnoughRoom(ByteList * list,short biggest)1045 static int isThereEnoughRoom(ByteList * list, short biggest){
1046   return ((list->maxSize-list->current) >= biggest);
1047 }
1048 /*
1049  * Tell how much room is left
1050  */
roomLeft(ByteList * list)1051 static short roomLeft(ByteList * list){
1052   return list->maxSize - list->current;
1053 }
1054 /*
1055  * Dump all commands to the printer and reset the structure
1056  *
1057  */
dumpToPrinter(ByteList * list,FILE * printStream)1058 static void dumpToPrinter(ByteList * list,FILE * printStream){
1059   short loopCounter;
1060   /* Actual dump */
1061   /* Please note that current is the first empty byte */
1062   for (loopCounter = 0; loopCounter < list->current; loopCounter++)
1063     {
1064       fputc(list->data[loopCounter],printStream);
1065     }
1066 
1067   /* Reset of the ByteList */
1068   list->current = 0;
1069 }
1070