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