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