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