Tizen 2.1 base
[platform/upstream/hplip.git] / prnt / hpijs / dj9xx.cpp
1 /*****************************************************************************\
2   dj9xx.cpp : Implimentation for the DJ9xx class
3
4   Copyright (c) 1996 - 2001, Hewlett-Packard Co.
5   All rights reserved.
6
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10   1. Redistributions of source code must retain the above copyright
11      notice, this list of conditions and the following disclaimer.
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15   3. Neither the name of Hewlett-Packard nor the names of its
16      contributors may be used to endorse or promote products derived
17      from this software without specific prior written permission.
18
19   THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
22   NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24   TO, PATENT INFRINGEMENT; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25   OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 \*****************************************************************************/
30
31
32 #if APDK_DJ9xx
33
34 #include "header.h"
35 #include "io_defs.h"
36 #include "dj8xx.h"
37 #include "dj9xx.h"
38 #include "resources.h"
39 #include "printerproxy.h"
40
41 APDK_BEGIN_NAMESPACE
42 extern BYTE* GetHT3x3_4();
43 extern BYTE* GetHT6x6_4_970();
44 APDK_END_NAMESPACE
45
46 APDK_BEGIN_NAMESPACE
47
48 extern uint32_t ulMapDJ970_KCMY[ 9 * 9 * 9 ];
49 extern uint32_t ulMapDJ970_KCMY_3x3x2[ 9 * 9 * 9 ];
50 extern uint32_t ulMapDJ970_Gossimer_Normal_KCMY[ 9 * 9 * 9 ];
51 extern uint32_t ulMapDJ600_CCM_K[ 9 * 9 * 9 ];
52 extern uint32_t ulMapGRAY_K_6x6x1[9 * 9 * 9];
53 extern uint32_t ulMapDJ970_Draft_KCMY[9 * 9 * 9];
54
55 extern void AsciiHexToBinary(BYTE* dest, char* src, int count);
56
57 DJ9xx::DJ9xx(SystemServices* pSS, BOOL proto)
58 : Printer(pSS,NUM_DJ6XX_FONTS,proto)
59 {
60     if (IOMode.bDevID)
61     {
62         bCheckForCancelButton = TRUE;
63         constructor_error = VerifyPenInfo();
64         CERRCHECK;
65     }
66     else ePen=BOTH_PENS;    // matches default mode
67
68
69     pMode[DEFAULTMODE_INDEX] = new DJ970Mode1();   // Normal Color
70     pMode[SPECIALMODE_INDEX] = new DJ970Mode2();   // Photo
71
72 #ifdef APDK_AUTODUPLEX
73 /*
74  *  When bidi is available, query printer for duplexer
75  *  For now, this is available only on Linux which is unidi only.
76  */
77
78     bDuplexCapable = TRUE;
79 #endif
80
81 #ifdef APDK_EXTENDED_MEDIASIZE
82     pMode[GRAYMODE_INDEX]      = new DJ970Mode3 ();   // Draft Grayscale K
83     pMode[SPECIALMODE_INDEX+1] = new DJ970Mode4 ();   // Normal Grayscale K
84     pMode[SPECIALMODE_INDEX+2] = new DJ970Mode5 ();   // Draft Color
85     pMode[SPECIALMODE_INDEX+3] = new DJ970ModePres();       // Best Color
86     pMode[SPECIALMODE_INDEX+4] = new DJ970ModePhotoPres();  // HiRes
87     ModeCount=7;
88 #else
89     pMode[GRAYMODE_INDEX]    = new GrayMode (ulMapDJ600_CCM_K);
90     ModeCount=3;
91 #endif
92
93     CMYMap = ulMapDJ970_KCMY;
94 }
95
96 DJ9xx::~DJ9xx()
97 { }
98
99 DJ970Mode1::DJ970Mode1()   // Normal Color
100 : PrintMode(ulMapDJ970_KCMY_3x3x2)
101 // 600x600x1 K
102 // 300x300x2 CMY
103 {
104
105     ColorDepth[K]=1;  // 600x600x1 K
106
107     for (int i=1; i < 4; i++)
108         ColorDepth[i]=2;    // 300x300x2 CMY
109
110     ResolutionX[K]=ResolutionY[K]=600;
111
112     MixedRes = TRUE;
113 #ifdef APDK_AUTODUPLEX
114     bDuplexCapable = TRUE;
115 #endif
116
117     ColorFEDTable = (BYTE*) HT300x3004level970_open;
118 }
119
120 DJ970Mode2::DJ970Mode2()    // Photo
121 : PrintMode(ulMapDJ970_Gossimer_Normal_KCMY)
122 // 600x600x1 K
123 // 600x600x2 CMY
124 {
125     int i;
126     ColorDepth[K]=1;  // 600x600x1 K
127
128     for (i=1; i < 4; i++)
129         ColorDepth[i]=2;    // 300x300x2 CMY
130
131     for (i=0; i < 4; i++)
132         ResolutionX[i]=ResolutionY[i]=600;
133
134     BaseResX = BaseResY = 600;
135     MixedRes = FALSE;
136
137     medium = mediaGlossy;
138
139     ColorFEDTable = GetHT6x6_4_970();
140
141 //    strcpy(ModeName, "Photo");
142     bFontCapable=FALSE;
143 #ifdef APDK_AUTODUPLEX
144     bDuplexCapable = FALSE;
145 #endif
146
147     pmQuality   = QUALITY_BEST;
148     pmMediaType = MEDIA_PHOTO;
149 }
150
151 #ifdef APDK_EXTENDED_MEDIASIZE
152 DJ970Mode3::DJ970Mode3 () : GrayMode (ulMapDJ600_CCM_K)   // Draft Grayscale K
153 {
154 #ifdef APDK_AUTODUPLEX
155     bDuplexCapable = TRUE;
156 #endif
157     pmQuality = QUALITY_DRAFT;
158     theQuality = qualityDraft;
159 }
160
161 DJ970Mode4::DJ970Mode4 () : PrintMode (ulMapGRAY_K_6x6x1)    // Normal Grayscale K
162 {
163     bFontCapable = FALSE;
164 #ifdef APDK_AUTODUPLEX
165     bDuplexCapable = TRUE;
166 #endif
167     ResolutionX[0] =
168     ResolutionY[0] = 600;
169     BaseResX = 600;
170     BaseResY = 600;
171     CompatiblePens[1] = BLACK_PEN;
172     pmQuality = QUALITY_NORMAL;
173     theQuality = qualityNormal;
174     dyeCount = 1;
175     pmColor = GREY_K;
176 }
177
178 DJ970Mode5::DJ970Mode5()    // Draft Color
179 : PrintMode(ulMapDJ970_Draft_KCMY)
180 // 300x300x1 K
181 // 300x300x1 CMY
182 {
183 #ifdef APDK_AUTODUPLEX
184     bDuplexCapable = TRUE;
185 #endif
186     pmQuality = QUALITY_DRAFT;
187     theQuality = qualityDraft;
188 }
189
190 // 2001.06.14 mrb: Added Presentation Mode: 600x600x2 for color,
191 //                                          600x600x1 for b/w
192 DJ970ModePres::DJ970ModePres() : PrintMode(ulMapDJ970_KCMY)
193 // 600x600x1 K
194 // 600x600x2 CMY
195 {
196     ColorDepth[K]=1;
197     
198     ColorDepth[C]=2;
199     ColorDepth[M]=2;
200     ColorDepth[Y]=2;
201
202     ResolutionX[K]=ResolutionY[K]=600;
203     ResolutionX[C]=ResolutionY[C]=600;
204     ResolutionX[M]=ResolutionY[M]=600;
205     ResolutionX[Y]=ResolutionY[Y]=600;
206
207     BaseResX = BaseResY = 600;
208     theQuality = qualityPresentation;
209
210     // 2001.07.09 mrb: Added for presentation mode of DJ970
211     ColorFEDTable = (BYTE*) HT600x600x4_Pres970_open;
212
213 #ifdef APDK_AUTODUPLEX
214     bDuplexCapable = TRUE;
215 #endif
216     pmQuality   = QUALITY_BEST;
217 }
218
219 // 2001.06.14 mrb: Added Presentation Photo Mode: 1200x1200x1 for color.
220 DJ970ModePhotoPres::DJ970ModePhotoPres() 
221 : PrintMode(ulMapDJ970_Gossimer_Normal_KCMY) 
222 // 1200x1200x1 CMY
223 {
224     ColorDepth[K]=1;
225     ColorDepth[C]=1;
226     ColorDepth[M]=1;
227     ColorDepth[Y]=1;
228
229     ResolutionX[K]=ResolutionY[K]=1200;
230     ResolutionX[C]=ResolutionY[C]=1200;
231     ResolutionX[M]=ResolutionY[M]=1200;
232     ResolutionX[Y]=ResolutionY[Y]=1200;
233
234     BaseResX = BaseResY = 1200;
235
236     medium = mediaGlossy;
237     theQuality = qualityPresentation;
238
239     // 2001.07.09 mrb: Added for presentation mode of DJ970
240     ColorFEDTable = (BYTE*) HT1200x1200x1_PhotoPres970_open;
241
242     bFontCapable=FALSE;
243     pmQuality   = QUALITY_HIGHRES_PHOTO;
244     pmMediaType = MEDIA_PHOTO;
245 }
246 #endif  // APDK_EXTENDED_MEDIASIZE
247
248 BOOL DJ9xx::UseGUIMode(PrintMode* pPrintMode)
249 {
250
251     if ((!pPrintMode->bFontCapable)
252 #ifdef APDK_AUTODUPLEX
253         || pPrintMode->QueryDuplexMode ()
254 #endif
255         )
256         return TRUE;
257     return FALSE;
258 }
259
260 Compressor* DJ9xx::CreateCompressor(unsigned int RasterSize)
261 {
262     return new Mode2(pSS,RasterSize);
263 }
264
265 Header900::Header900(Printer* p,PrintContext* pc)
266     : Header895(p,pc)
267 { }
268
269 Header* DJ9xx::SelectHeader(PrintContext* pc)
270 {
271     return new Header900(this,pc);
272 }
273
274 DRIVER_ERROR Header900::Send()
275 {
276     DRIVER_ERROR err;
277     //BOOL bDuplex = FALSE;
278
279     StartSend();
280
281     // this code will look for the duplexer enabled in the device ID and send the right
282     // escape to the printer to enable duplexing.  At this time, however, we are not
283     // going to support duplexing.  One, it is not supported with PCL3, which we need
284     // for device font support.  Second, we don't have the resources to reformat the page
285     // for book duplexing and can only do tablet.
286
287     /*BYTE bDevIDBuff[DevIDBuffSize];
288     err = theTranslator->pSS->GetDeviceID(bDevIDBuff, DevIDBuffSize, TRUE);
289     ERRCHECK;
290
291     // look for duplex code in bDevIDBuff
292     Duplex = DuplexEnabled(bDevIDBuff);
293
294     if(bDuplex)
295     {
296         err = thePrinter->Send((const BYTE*)EnableDuplex,sizeof(EnableDuplex));
297         ERRCHECK;
298     }*/
299
300 #ifdef APDK_AUTODUPLEX
301     if (thePrintContext->QueryDuplexMode () != DUPLEXMODE_NONE)
302         err = thePrinter->Send ((const BYTE *) EnableDuplex, sizeof (EnableDuplex));
303 #endif
304
305     err = ConfigureRasterData();
306     ERRCHECK;
307
308     err=Graphics();     // start raster graphics and set compression mode
309
310     return err;
311 }
312
313 BOOL Header900::DuplexEnabled(BYTE* bDevIDBuff)
314 {
315     char* pStrVstatus = NULL;
316     char* pStrDuplex = NULL;
317     char* pStrSemicolon = NULL;
318
319     if((pStrVstatus = strstr((char*)bDevIDBuff + 2,"VSTATUS:")))
320         pStrVstatus += 8;
321     else
322         return FALSE;
323
324     pStrDuplex = pStrVstatus;
325     pStrSemicolon = pStrVstatus;
326
327     // now parse VSTATUS parameters to find if we are in simplex or duplex
328     if (!(pStrSemicolon = strstr((char*)pStrVstatus,";")))
329         return FALSE;
330
331     if ( (pStrDuplex = strstr((char*)pStrVstatus,"DP")) )
332         if(pStrDuplex < pStrSemicolon)
333             return TRUE;
334     if ( (pStrDuplex = strstr((char*)pStrVstatus,"SM")) )
335         if(pStrDuplex < pStrSemicolon)
336             return FALSE;
337
338     DBG1("didn't find SM or DP!!\n");
339     return FALSE;
340 }
341
342
343 BYTE DJ9xx::PhotoTrayStatus
344 (
345     BOOL bQueryPrinter
346 )
347 {
348     DRIVER_ERROR err;
349     char* pStrVstatus = NULL;
350     char* pStrPhotoTray = NULL;
351     char* pStrSemicolon = NULL;
352
353     BYTE bDevIDBuff[DevIDBuffSize];
354
355     if (!IOMode.bDevID)
356     {
357         bQueryPrinter = FALSE;
358     }
359
360     err=pSS->GetDeviceID(bDevIDBuff, DevIDBuffSize, bQueryPrinter);
361     if (err!=NO_ERROR)
362     {
363         return 0;
364     }
365
366     if((pStrVstatus = strstr((char*)bDevIDBuff + 2,"VSTATUS:")))
367     {
368         pStrVstatus += 8;
369     }
370     else
371     {
372         return 0;
373     }
374
375     pStrPhotoTray = pStrVstatus;
376     pStrSemicolon = pStrVstatus;
377
378     // now parse VSTATUS parameters to find if we are in simplex or duplex
379     if (!(pStrSemicolon = strstr((char*)pStrVstatus,";")))
380     {
381         return 0;
382     }
383
384     if ( (pStrPhotoTray = strstr((char*)pStrVstatus,"PH")) )
385     {
386         if(pStrPhotoTray < pStrSemicolon)
387         {
388             return '9';  // return same as VIP installed and engaged status
389         }
390     }
391     if ( (pStrPhotoTray = strstr((char*)pStrVstatus,"NR")) )
392     {
393         if(pStrPhotoTray < pStrSemicolon)
394         {
395             return 0;
396         }
397     }
398
399     DBG1("didn't find PH or NR!!\n");
400     return 0;
401 } //PhotoTrayStatus
402
403 BOOL DJ9xx::PhotoTrayPresent
404 (
405     BOOL bQueryPrinter
406 )
407 {
408     // present (and not engaged) == 8
409     return ((PhotoTrayStatus(bQueryPrinter) & 8) == 8);
410 } //PhotoTrayInstalled
411
412
413 PHOTOTRAY_STATE DJ9xx::PhotoTrayEngaged
414 (
415     BOOL bQueryPrinter
416 )
417 {
418     // present and engaged == 9
419     return ((PHOTOTRAY_STATE) ((PhotoTrayStatus(bQueryPrinter) & 9) == 9));
420 } //PhotoTrayEngaged
421
422
423 PAPER_SIZE DJ9xx::MandatoryPaperSize()
424 {
425     if (PhotoTrayEngaged (TRUE))
426     {
427         return PHOTO_SIZE;
428     }
429     else
430     {
431         return UNSUPPORTED_SIZE;   // code for "nothing mandatory"
432     }
433 } //MandatoryPaperSize
434
435
436 DISPLAY_STATUS DJ9xx::ParseError(BYTE status_reg)
437 {
438     DBG1("DJ9XX, parsing error info\n");
439
440     DRIVER_ERROR err = NO_ERROR;
441     BYTE DevIDBuffer[DevIDBuffSize];
442
443     char *pStr;
444     if(IOMode.bDevID)
445     {
446         // If a bi-di cable was plugged in and everything was OK, let's see if it's still
447         // plugged in and everything is OK
448         err = pSS->GetDeviceID (DevIDBuffer, DevIDBuffSize, TRUE);
449         if(err != NO_ERROR)
450         {
451             // job was bi-di but now something's messed up, probably cable unplugged
452             return DISPLAY_COMM_PROBLEM;
453         }
454
455         if ( (pStr=(char *)strstr((const char*)DevIDBuffer+2,"VSTATUS:")) == NULL )
456         {
457             return DISPLAY_COMM_PROBLEM;
458         }
459
460         pStr+=8;   // skip "VSTATUS:"
461         
462                 // Paper Jam or Paper Stall
463         if (strstr((char*)pStr,"PAJM") || strstr((char*)pStr,"PAPS"))
464         {
465             return DISPLAY_PAPER_JAMMED;
466         }
467
468                 // Carriage Stall
469         if (strstr((char*)pStr,"CARS"))
470         {
471             return DISPLAY_ERROR_TRAP;
472         }
473
474         if (strstr((char*)pStr,"OOPA"))     // OOP state
475         {
476             DBG1("Out of Paper [from Encoded DevID]\n");
477             return DISPLAY_OUT_OF_PAPER;
478         }
479
480                 //  Job Cancelled (AIO printer turn idle after job canceled)
481         if (strstr((char*)pStr,"CNCL"))     // CNCL state
482         {
483             DBG1("Printing Canceled [from Encoded DevID]\n");
484             return DISPLAY_PRINTING_CANCELED;
485         }
486
487         if ( TopCoverOpen(status_reg) )
488         {
489             DBG1("Top Cover Open\n");
490             return DISPLAY_TOP_COVER_OPEN;
491         }
492
493         // VerifyPenInfo will handle prompting the user
494         // if this is a problem
495         err = VerifyPenInfo();
496
497         if(err != NO_ERROR)
498             // VerifyPenInfo returned an error, which can only happen when ToDevice
499             // or GetDeviceID returns an error. Either way, it's BAD_DEVICE_ID or
500             // IO_ERROR, both unrecoverable.  This is probably due to the printer
501             // being turned off during printing, resulting in us not being able to
502             // power it back on in VerifyPenInfo, since the buffer still has a
503             // partial raster in it and we can't send the power-on command.
504             return DISPLAY_COMM_PROBLEM;
505     }
506
507     // check for errors we can detect from the status reg
508     if (IOMode.bStatus)
509     {
510         if ( DEVICE_IS_OOP(status_reg) )
511         {
512             DBG1("Out Of Paper\n");
513             return DISPLAY_OUT_OF_PAPER;
514         }
515
516         if (DEVICE_PAPER_JAMMED(status_reg))
517         {
518             DBG1("Paper Jammed\n");
519             return DISPLAY_PAPER_JAMMED;
520         }
521         if (DEVICE_IO_TRAP(status_reg))
522         {
523             DBG1("IO trap\n");
524             return DISPLAY_ERROR_TRAP;
525         }
526     }
527
528     // don't know what the problem is-
529     //  Is the PrinterAlive?
530     if (pSS->PrinterIsAlive())
531     {
532         iTotal_SLOW_POLL_Count += iMax_SLOW_POLL_Count;
533 #if defined(DEBUG) && (DBG_MASK & DBG_LVL1)
534         printf("iTotal_SLOW_POLL_Count = %d\n",iTotal_SLOW_POLL_Count);
535 #endif
536         // -Note that iTotal_SLOW_POLL_Count is a multiple of
537         //  iMax_SLOW_POLL_Count allowing us to check this
538         //  on an absolute time limit - not relative to the number
539         //  of times we happen to have entered ParseError.
540         // -Also note that we have different thresholds for uni-di & bi-di.
541         if(
542             ((IOMode.bDevID == FALSE) && (iTotal_SLOW_POLL_Count >= 60)) ||
543             ((IOMode.bDevID == TRUE)  && (iTotal_SLOW_POLL_Count >= 120))
544           )
545             return DISPLAY_BUSY;
546         else return DISPLAY_PRINTING;
547     }
548     else
549         return DISPLAY_COMM_PROBLEM;
550 }
551
552 DRIVER_ERROR DJ9xx::VerifyPenInfo()
553 {
554
555     DRIVER_ERROR err=NO_ERROR;
556
557     if(IOMode.bDevID == FALSE)
558         return err;
559
560     err = ParsePenInfo(ePen);
561
562     if(err == UNSUPPORTED_PEN) // probably Power Off - pens couldn't be read
563     {
564         DBG1("DJ9xx::Need to do a POWER ON to get penIDs\n");
565
566         // have to delay for DJ9xx or the POWER ON will be ignored
567         if (pSS->BusyWait((DWORD)2000) == JOB_CANCELED)
568             return JOB_CANCELED;
569
570         DWORD length=sizeof(DJ895_Power_On);
571         err = pSS->ToDevice(DJ895_Power_On,&length);
572         ERRCHECK;
573
574         err = pSS->FlushIO();
575         ERRCHECK;
576
577         // give the printer some time to power up
578         if (pSS->BusyWait ((DWORD) 2500) == JOB_CANCELED)
579             return JOB_CANCELED;
580
581         err = ParsePenInfo(ePen);
582     }
583
584     ERRCHECK;
585
586     // check for the normal case
587     if (ePen == BOTH_PENS)
588         return NO_ERROR;
589
590     while ( ePen != BOTH_PENS   )
591     {
592
593         switch (ePen)
594         {
595             case BLACK_PEN:
596                 // black pen installed, need to install color pen
597                 pSS->DisplayPrinterStatus(DISPLAY_NO_COLOR_PEN);
598                 break;
599             case COLOR_PEN:
600                 // color pen installed, need to install black pen
601                 pSS->DisplayPrinterStatus(DISPLAY_NO_BLACK_PEN);
602                 break;
603             case NO_PEN:
604                 // neither pen installed
605             default:
606                 pSS->DisplayPrinterStatus(DISPLAY_NO_PENS);
607                 break;
608         }
609
610         if (pSS->BusyWait(500) == JOB_CANCELED)
611             return JOB_CANCELED;
612
613         err =  ParsePenInfo(ePen);
614         ERRCHECK;
615     }
616
617     pSS->DisplayPrinterStatus(DISPLAY_PRINTING);
618
619     return NO_ERROR;
620
621 }
622
623 DRIVER_ERROR DJ9xx::ParsePenInfo(PEN_TYPE& ePen, BOOL QueryPrinter)
624 {
625     char    *str;
626     DRIVER_ERROR err = SetPenInfo (str, QueryPrinter);
627     ERRCHECK;
628
629     if (*str != '$')
630     {
631                 // DeskJet 9300 has DJ990 style devid string.
632                 int num_pens = 0;
633                 PEN_TYPE temp_pen1 = NO_PEN;
634                 BYTE penInfoBits[4];
635                 int iNumMissingPens = 0;
636
637                 // the first byte indicates how many pens are supported
638                 if ((str[0] >= '0') && (str[0] <= '9'))
639                 {
640                         num_pens = str[0] - '0';
641                 }
642                 else if ((str[0] >= 'A') && (str[0] <= 'F'))
643                 {
644                         num_pens = 10 + (str[0] - 'A');
645                 }
646                 else
647                 {
648                         return BAD_DEVICE_ID;
649                 }
650
651                 if ((int) strlen (str) < (num_pens * 8))
652                 {
653                         return BAD_DEVICE_ID;
654                 }
655
656                 //  DJ990 style DevID
657
658                 if (pSS->GetVIPVersion () == 1)
659                 {
660                         if (num_pens < 2)
661                         {
662                                 return UNSUPPORTED_PEN;
663                         }
664
665                         // parse pen1 (should be black)
666                         AsciiHexToBinary(penInfoBits, str+1, 4);
667                         penInfoBits[1] &= 0xf8; // mask off ink level trigger bits
668
669                         if ((penInfoBits[0] == 0xc1) && (penInfoBits[1] == 0x10))
670                         {   // black
671                                 temp_pen1 = BLACK_PEN;
672                         }
673                         else if (penInfoBits[0] == 0xc0)
674                         {   // missing pen
675                                 temp_pen1 = NO_PEN;
676                                 iNumMissingPens = 1;
677                         }
678                         else
679                         {
680                                 return UNSUPPORTED_PEN;
681                         }
682
683                         // now check pen2 (should be color)
684                         AsciiHexToBinary(penInfoBits, str+9, 4);
685                         penInfoBits[1] &= 0xf8; // mask off ink level trigger bits
686
687                         if ((penInfoBits[0] == 0xc2) && (penInfoBits[1] == 0x08))
688                         {   // Chinook
689                                 if (temp_pen1 == BLACK_PEN)
690                                 {
691                                         ePen = BOTH_PENS;
692                                 }
693                                 else
694                                 {
695                                         ePen = COLOR_PEN;
696                                 }
697                         }
698                         else if (penInfoBits[0] == 0xc0)
699                         {   // missing pen
700                                 ePen = temp_pen1;
701                                 iNumMissingPens = 1;
702                         }
703                         else
704                         {
705                                 return UNSUPPORTED_PEN;
706                         }
707
708                         return NO_ERROR;
709                 }
710
711         //  Check for missing pens
712
713                 if (*(str - 1) == '1' && *(str - 2) == '1')
714                 {
715                         return UNSUPPORTED_PEN;
716                 }
717
718                 char    *p = str + 1;
719                 BYTE    penColor;
720
721                 ePen = NO_PEN;
722
723                 for (int i = 0; i < num_pens; i++, p += 8)
724                 {
725                         AsciiHexToBinary (penInfoBits, p, 8);
726
727                         if ((penInfoBits[1] & 0xf8) == 0xf8)
728                         {
729
730         //          The high 5 bits in the 3rd and 4th nibble (second byte) identify the
731         //          installed pen. If all 5 bits are on, user has installed an incompatible pen.
732
733                                 return UNSUPPORTED_PEN;
734                         }
735
736                         if ((penInfoBits[0] & 0x80) != 0x80)        // if Bit 31 is 0, this is not a pen
737                         {
738                                 continue;
739                         }
740                         penColor = penInfoBits[0] & 0x3F;
741                         switch (penColor)
742                         {
743                                 case 0:
744                                 {
745                                         iNumMissingPens++;
746                                         break;
747                                 }
748                                 case 1:
749                                         ePen = BLACK_PEN;
750                                         break;
751                                 case 2:
752                                 {
753                                         if (ePen == BLACK_PEN)
754                                         {
755                                                 ePen = BOTH_PENS;
756                                         }
757                                         else
758                                         {
759                                                 ePen = COLOR_PEN;
760                                         }
761                                         break;
762                                 }
763                                 case 4:             // cyan pen
764                                 case 5:             // magenta pen
765                                 case 6:             // yellow pen
766                                 case 7:             // low dye load cyan pen
767                                 case 8:             // low dye load magenta pen
768                                 case 9:             // low dye load yellow pen
769                                         if (ePen == BLACK_PEN || ePen == BOTH_PENS)
770                                         {
771                                                 ePen = BOTH_PENS;
772                                         }
773                                         else
774                                         {
775                                                 ePen = COLOR_PEN;
776                                         }
777                                         break;
778                                 default:
779                                         ePen = UNKNOWN_PEN;
780                         }
781                 }
782                 return NO_ERROR;
783     }
784
785         // parse penID
786         PEN_TYPE temp_pen1;
787         // check pen1, assume it is black, pen2 is color
788         switch (str[1])
789         {
790                 case 'H':
791                 case 'L':
792                 case 'Z':
793                         temp_pen1 = BLACK_PEN;
794                         break;
795                 case 'X': return UNSUPPORTED_PEN;
796                 default:  temp_pen1 = NO_PEN; break;
797         }
798
799         // now check pen2
800
801         int i = 2;
802         while ((i < DevIDBuffSize) && str[i]!='$') i++; // handles variable length penIDs
803         if (i == DevIDBuffSize)
804         {
805                 return BAD_DEVICE_ID;
806         }
807
808         i++;
809
810         // need to be more forgiving of the color pen type because of
811         // the unknown chinookID for DJ970
812         // we can't guarantee the (F)lash color pen, but we can make sure
813         // the pen is not (X)Undefined, (A)Missing or (M)onet
814         if(str[i]!='X' && str[i]!='A' && str[i]!='M')
815         // check what pen1 was
816         {
817                 if (temp_pen1 == BLACK_PEN)
818                                 ePen = BOTH_PENS;
819                 else
820                 {
821                                 ePen = COLOR_PEN;
822                 }
823         }
824         else // no color pen, just set what pen1 was
825                 ePen = temp_pen1;
826
827         return NO_ERROR;
828 }
829
830 #if defined(APDK_FONTS_NEEDED)
831 Font* DJ9xx::RealizeFont(const int index,const BYTE bSize,
832                            const TEXTCOLOR eColor,
833                            const BOOL bBold,const BOOL bItalic,
834                            const BOOL bUnderline)
835
836 {
837
838     return Printer::RealizeFont(index,bSize,eColor,bBold,bItalic,bUnderline);
839 }
840 #endif
841
842 DRIVER_ERROR DJ9xx::CleanPen()
843 {
844     const BYTE DJ970_User_Output_Page[] = {ESC, '%','P','u','i','f','p','.',
845         'm','u','l','t','i','_','b','u','t','t','o','n','_','p','u','s','h',' ','3',';',
846         'u','d','w','.','q','u','i','t',';',ESC,'%','-','1','2','3','4','5','X' };
847
848     DWORD length = sizeof(PEN_CLEAN_PML);
849     DRIVER_ERROR err = pSS->ToDevice(PEN_CLEAN_PML, &length);
850     ERRCHECK;
851
852     // send this page so that the user sees some output.  If you don't send this, the
853     // pens get serviced but nothing prints out.
854     length = sizeof(DJ970_User_Output_Page);
855     return pSS->ToDevice(DJ970_User_Output_Page, &length);
856 }
857
858 APDK_END_NAMESPACE
859
860 #endif  // APDK_DJ9xx