Tizen 2.1 base
[platform/upstream/hplip.git] / prnt / hpijs / quickconnect.cpp
1 /*****************************************************************************\\r
2   quickconnect.cpp : Implementation for the QuickConnect class\r
3 \r
4   Copyright (c) 2008, Hewlett-Packard Co.\r
5   All rights reserved.\r
6 \r
7   Redistribution and use in source and binary forms, with or without\r
8   modification, are permitted provided that the following conditions\r
9   are met:\r
10   1. Redistributions of source code must retain the above copyright\r
11      notice, this list of conditions and the following disclaimer.\r
12   2. Redistributions in binary form must reproduce the above copyright\r
13      notice, this list of conditions and the following disclaimer in the\r
14      documentation and/or other materials provided with the distribution.\r
15   3. Neither the name of Hewlett-Packard nor the names of its\r
16      contributors may be used to endorse or promote products derived\r
17      from this software without specific prior written permission.\r
18 \r
19   THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED\r
20   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
21   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN\r
22   NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
23   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\r
24   TO, PATENT INFRINGEMENT; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\r
25   OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
26   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
27   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
28   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
29 \*****************************************************************************/\r
30 \r
31 \r
32 #ifdef APDK_QUICKCONNECT\r
33 \r
34 #include "header.h"\r
35 #include "io_defs.h"\r
36 #include "quickconnect.h"\r
37 #include "printerproxy.h"\r
38 #include "resources.h"\r
39 #include <setjmp.h>\r
40 \r
41 APDK_BEGIN_NAMESPACE\r
42 \r
43 #define MAX_JPEG_FILE_SIZE 2097152\r
44 \r
45 QuickConnect::QuickConnect (SystemServices* pSS, BOOL proto)\r
46     : Printer (pSS, proto)\r
47 {\r
48 \r
49     if ((!proto) && (IOMode.bDevID))\r
50     {\r
51         constructor_error = VerifyPenInfo ();\r
52         CERRCHECK;\r
53     }\r
54     else\r
55         ePen = BOTH_PENS;    // matches default mode\r
56 \r
57     ModeCount = 0;\r
58     pMode[ModeCount++] = new QCAutomatic ();\r
59     pMode[ModeCount++] = new QCFastNormal ();\r
60     pMode[ModeCount++] = new QCNormal ();\r
61     pMode[ModeCount++] = new QCBest ();\r
62 \r
63     CMYMap = NULL;\r
64 \r
65     m_iJpegBufferPos = 0;\r
66     m_iMaxFileSize = MAX_JPEG_FILE_SIZE;\r
67     m_iInputBufferSize = 0;\r
68     m_pbyJpegBuffer = NULL;\r
69     m_pbyInputBuffer = NULL;\r
70 \r
71     m_iPhotoFix = 0;\r
72     m_iRemoveRedEye = 1;\r
73     m_iJobId = pSS->GetSystemTickCount ();\r
74 }\r
75 \r
76 QuickConnect::~QuickConnect ()\r
77 {\r
78     if (m_pbyJpegBuffer)\r
79     {\r
80         pSS->FreeMemory (m_pbyJpegBuffer);\r
81     }\r
82     if (m_pbyInputBuffer)\r
83     {\r
84         pSS->FreeMemory (m_pbyInputBuffer);\r
85     }\r
86 }\r
87 \r
88 QCAutomatic::QCAutomatic ()\r
89 : PrintMode (NULL)\r
90 {\r
91     ResolutionX[0] = 300;\r
92     ResolutionY[0] = 300;\r
93     BaseResX = BaseResY = 300;\r
94 \r
95     bFontCapable = FALSE;\r
96 \r
97         theQuality = qualityAuto;\r
98     medium     = mediaAuto;\r
99 \r
100     pmQuality = QUALITY_AUTO;\r
101     pmMediaType   = MEDIA_PLAIN;\r
102         Config.bColorImage = FALSE;\r
103     Config.bCompress = FALSE;\r
104 #ifdef APDK_AUTODUPLEX\r
105     bDuplexCapable = FALSE;\r
106 #endif\r
107 }\r
108 \r
109 QCFastNormal::QCFastNormal ()\r
110 : PrintMode (NULL)\r
111 {\r
112     ResolutionX[0] = 300;\r
113     ResolutionY[0] = 300;\r
114     BaseResX = BaseResY = 300;\r
115 \r
116     bFontCapable = FALSE;\r
117 \r
118         theQuality = qualityFastNormal;\r
119     medium     = mediaAuto;\r
120 \r
121     pmQuality = QUALITY_FASTNORMAL;\r
122     pmMediaType   = MEDIA_PLAIN;\r
123         Config.bColorImage = FALSE;\r
124     Config.bCompress = FALSE;\r
125 #ifdef APDK_AUTODUPLEX\r
126     bDuplexCapable = FALSE;\r
127 #endif\r
128 }\r
129 \r
130 QCNormal::QCNormal ()\r
131 : PrintMode (NULL)\r
132 {\r
133     ResolutionX[0] = 300;\r
134     ResolutionY[0] = 300;\r
135     BaseResX = BaseResY = 300;\r
136 \r
137     bFontCapable = FALSE;\r
138 \r
139         theQuality = qualityNormal;\r
140     medium     = mediaAuto;\r
141 \r
142     pmQuality = QUALITY_NORMAL;\r
143     pmMediaType   = MEDIA_PLAIN;\r
144         Config.bColorImage = FALSE;\r
145     Config.bCompress = FALSE;\r
146 #ifdef APDK_AUTODUPLEX\r
147     bDuplexCapable = FALSE;\r
148 #endif\r
149 }\r
150 \r
151 QCBest::QCBest ()\r
152 : PrintMode (NULL)\r
153 {\r
154     ResolutionX[0] = 300;\r
155     ResolutionY[0] = 300;\r
156     BaseResX = BaseResY = 300;\r
157 \r
158     bFontCapable = FALSE;\r
159 \r
160         theQuality = qualityPresentation;\r
161     medium     = mediaAuto;\r
162 \r
163     pmQuality = QUALITY_BEST;\r
164     pmMediaType   = MEDIA_PLAIN;\r
165         Config.bColorImage = FALSE;\r
166     Config.bCompress = FALSE;\r
167 #ifdef APDK_AUTODUPLEX\r
168     bDuplexCapable = FALSE;\r
169 #endif\r
170 }\r
171 \r
172 Header *QuickConnect::SelectHeader (PrintContext *pc)\r
173 {\r
174     m_iRowNumber = 0;\r
175     m_pPC = pc;\r
176     m_iRowWidth = pc->OutputPixelsPerRow () * 3;\r
177 \r
178     m_iInputBufferSize = m_iRowWidth * (int) (pc->PhysicalPageSizeY () * pc->EffectiveResolutionY ());\r
179     m_pbyInputBuffer = (BYTE *) pSS->AllocMem (m_iInputBufferSize);\r
180     m_pbyJpegBuffer = (BYTE *) pSS->AllocMem (m_iMaxFileSize);\r
181     if (m_pbyInputBuffer == NULL || m_pbyJpegBuffer == NULL)\r
182     {\r
183         constructor_error = ALLOCMEM_ERROR;\r
184         return NULL;\r
185     }\r
186     memset (m_pbyInputBuffer, 0xFF, m_iInputBufferSize);\r
187     return new HeaderQuickConnect (this, pc);\r
188 }\r
189 \r
190 DRIVER_ERROR QuickConnect::Encapsulate (const RASTERDATA* InputRaster, BOOL bLastPlane)\r
191 {\r
192     memcpy (m_pbyInputBuffer + (m_iRowNumber * m_iRowWidth), InputRaster->rasterdata[COLORTYPE_COLOR],\r
193             InputRaster->rastersize[COLORTYPE_COLOR]);\r
194     m_iRowNumber++;\r
195     return NO_ERROR;\r
196 }\r
197 \r
198 DRIVER_ERROR QuickConnect::ParsePenInfo (PEN_TYPE& ePen, BOOL QueryPrinter)\r
199 {\r
200     char        *str;\r
201     int         num_pens = 0;\r
202     ePen = BOTH_PENS;\r
203 \r
204     DRIVER_ERROR err = SetPenInfo (str, QueryPrinter);\r
205     if (err != NO_ERROR)\r
206     {\r
207         return NO_ERROR;\r
208     }\r
209 \r
210     num_pens = str[0] - '0';\r
211     if (num_pens == 0)\r
212     {\r
213         ePen = NO_PEN;\r
214         return NO_ERROR;\r
215     }\r
216 \r
217     if ((int) strlen (str) < (num_pens * 8))\r
218     {\r
219         return BAD_DEVICE_ID;\r
220     }\r
221 \r
222     return NO_ERROR;\r
223 }\r
224 \r
225 DRIVER_ERROR QuickConnect::VerifyPenInfo ()\r
226 {\r
227     ePen = BOTH_PENS;\r
228     DRIVER_ERROR err = NO_ERROR;\r
229 \r
230     if (IOMode.bDevID == FALSE)\r
231         return err;\r
232 \r
233     ePen = NO_PEN;\r
234 \r
235     err = ParsePenInfo (ePen);\r
236 \r
237     if(err == UNSUPPORTED_PEN) // probably Power Off - pens couldn't be read\r
238     {\r
239 \r
240         // have to delay or the POWER ON will be ignored\r
241         if (pSS->BusyWait ((DWORD) 2000) == JOB_CANCELED)\r
242         {\r
243             return JOB_CANCELED;\r
244         }\r
245 \r
246         DWORD length = sizeof (DJ895_Power_On);\r
247         err = pSS->ToDevice (DJ895_Power_On, &length);\r
248         ERRCHECK;\r
249 \r
250         err = pSS->FlushIO ();\r
251         ERRCHECK;\r
252 \r
253         // give the printer some time to power up\r
254         if (pSS->BusyWait ((DWORD) 2500) == JOB_CANCELED)\r
255         {\r
256             return JOB_CANCELED;\r
257         }\r
258 \r
259         err = ParsePenInfo (ePen);\r
260     }\r
261 \r
262     ERRCHECK;\r
263     while (ePen == NO_PEN)\r
264     {\r
265         pSS->DisplayPrinterStatus (DISPLAY_NO_PENS);\r
266 \r
267         if (pSS->BusyWait (500) == JOB_CANCELED)\r
268         {\r
269             return JOB_CANCELED;\r
270         }\r
271         err =  ParsePenInfo (ePen);\r
272         ERRCHECK;\r
273     }\r
274 \r
275     pSS->DisplayPrinterStatus (DISPLAY_PRINTING);\r
276 \r
277     return err;\r
278 }\r
279 \r
280 int QuickConnect::MapPaperSize (PAPER_SIZE ePaperSize)\r
281 {\r
282     switch (ePaperSize)\r
283     {\r
284         case PHOTO_5x7:\r
285             return 2;\r
286         case L:\r
287             return 3;\r
288         case PHOTO_4x8:\r
289             return 4;\r
290         case PHOTO_4x12:\r
291             return 5;\r
292         case CARD_4x6:\r
293         case HAGAKI:\r
294         case A6:\r
295             return 1;\r
296         default:\r
297             return 0;\r
298     }\r
299 }\r
300 \r
301 int QuickConnect::MapMediaType (MEDIATYPE eMediaType)\r
302 {\r
303     switch (eMediaType)\r
304     {\r
305         case MEDIA_HIGHRES_PHOTO:\r
306             return 1;\r
307         case MEDIA_PREMIUM:\r
308             return 2;\r
309         case MEDIA_PLAIN:\r
310             return 3;\r
311         case MEDIA_PHOTO:\r
312             return 4;\r
313         case MEDIA_AUTO:\r
314         default:\r
315             return 0;\r
316     }\r
317 }\r
318 \r
319 int QuickConnect::MapQualityMode (QUALITY_MODE eQualityMode)\r
320 {\r
321     switch (eQualityMode)\r
322     {\r
323         case QUALITY_NORMAL:\r
324             return 2;\r
325         case QUALITY_FASTNORMAL:\r
326             return 3;\r
327         case QUALITY_BEST:\r
328             return 1;\r
329         case QUALITY_AUTO:\r
330         default:\r
331             return 0;\r
332     }\r
333 }\r
334 \r
335 DRIVER_ERROR QuickConnect::SetHint (PRINTER_HINT eHint, int iValue)\r
336 {\r
337     switch (eHint)\r
338     {\r
339         case PHOTO_FIX_HINT:\r
340         {\r
341             m_iPhotoFix = iValue & 0x1;\r
342             break;\r
343         }\r
344         case RED_EYE_REMOVAL_HINT:\r
345         {\r
346             m_iRemoveRedEye = iValue & 0x1;\r
347             break;\r
348         }\r
349         case MAX_FILE_SIZE_HINT:\r
350         {\r
351             m_iMaxFileSize = iValue;\r
352             break;\r
353         }\r
354         default:\r
355             break;\r
356     }\r
357     return NO_ERROR;\r
358 }\r
359 \r
360 \r
361 DRIVER_ERROR    QuickConnect::Flush (int iBufferSize)\r
362 {\r
363     DRIVER_ERROR    err = NO_ERROR;\r
364     if (iBufferSize == -1 && m_iRowNumber > 0)\r
365     {\r
366         char    szStr[256];\r
367         int     iPaperSize, iMediaType, iPrintQuality;\r
368         char    szCopiesStr[64];\r
369         char    szNullBytes[632];\r
370         static  const char    szPJLHeader[] = "\x1B\x45\x1B%-12345X@PJL ENTER LANGUAGE=PHOTOJPEG\012";\r
371         static  const char    szPJLEndJob[] = "\x1B\x45\x1B%-12345X";\r
372 \r
373         err = Compress ();\r
374         ERRCHECK;\r
375 \r
376         memset (szNullBytes, 0, sizeof (szNullBytes));\r
377         sprintf (szCopiesStr, "@PJL SET COPIES=%d\012@PJL SET JOBID=%d\012", m_pPC->GetCopyCount (), m_iJobId);\r
378 \r
379         Send ((const BYTE *) szNullBytes, 600);\r
380         Send ((const BYTE *) szPJLHeader, sizeof(szPJLHeader) - 1);\r
381         Send ((const BYTE *) szCopiesStr, strlen (szCopiesStr));\r
382 \r
383         COLORMODE       eColorMode = COLOR;\r
384         MEDIATYPE       eMediaType;\r
385         QUALITY_MODE    eQualityMode;\r
386         BOOL            bDeviceText;\r
387 \r
388         m_pPC->GetPrintModeSettings (eQualityMode, eMediaType, eColorMode, bDeviceText);\r
389         iMediaType = MapMediaType (eMediaType);\r
390         iPrintQuality = MapQualityMode (eQualityMode);\r
391         iPaperSize = MapPaperSize (m_pPC->GetPaperSize ());\r
392 \r
393         sprintf (szStr,\r
394                  "@PJL SET PAPER=%d\012@PJL SET MEDIATYPE=%d\012@PJL SET PRINTQUALITY=%d\012@PJL SET BORDERLESS=%d\012",\r
395                  iPaperSize, iMediaType, iPrintQuality, m_pPC->IsBorderless ());\r
396         err = Send ((const BYTE *) szStr, strlen (szStr));\r
397 \r
398         err = AddExifHeader ();\r
399         ERRCHECK;\r
400 \r
401         memset (m_pbyInputBuffer, 0xFF, m_iInputBufferSize);\r
402 \r
403 //      Send the END JOB PJL command\r
404 \r
405         Send ((const BYTE *) szPJLEndJob, sizeof(szPJLEndJob) - 1); \r
406 \r
407         m_iRowNumber = 0;\r
408     }\r
409     return err;\r
410 }\r
411 \r
412 HeaderQuickConnect::HeaderQuickConnect (Printer *p, PrintContext *pc)\r
413     : Header (p,pc)\r
414\r
415 }\r
416 \r
417 DRIVER_ERROR HeaderQuickConnect::Send ()\r
418 {\r
419     return NO_ERROR;\r
420 }\r
421 \r
422 DRIVER_ERROR HeaderQuickConnect::EndJob ()\r
423 {\r
424     DRIVER_ERROR    err = NO_ERROR;\r
425 \r
426     return err;\r
427 }\r
428 \r
429 DRIVER_ERROR HeaderQuickConnect::FormFeed ()\r
430 {\r
431         DRIVER_ERROR    err = NO_ERROR;\r
432     thePrinter->Flush (-1);\r
433         return err;\r
434 }\r
435 \r
436 DRIVER_ERROR HeaderQuickConnect::SendCAPy (unsigned int iAbsY)\r
437 {\r
438     return NO_ERROR;\r
439 }\r
440 \r
441 void HPFlush_output_buffer_callback (JOCTET *outbuf, BYTE *buffer, int size)\r
442 {\r
443     QuickConnect    *pQC = (QuickConnect *) outbuf;\r
444     pQC->JpegData (buffer, size);\r
445 }\r
446 \r
447 void QuickConnect::JpegData (BYTE *buffer, int iSize)\r
448 {\r
449     m_iJpegBufferPos += iSize;\r
450     if (m_iJpegBufferPos < m_iMaxFileSize)\r
451     {\r
452         memcpy (m_pbyJpegBuffer + m_iJpegBufferPos - iSize, buffer, iSize);\r
453     }\r
454     else\r
455     {\r
456         m_iJpegBufferPos = m_iMaxFileSize + 1;\r
457     }\r
458 }\r
459 \r
460 //----------------------------------------------------------------\r
461 // These are "overrides" to the JPEG library error routines\r
462 //----------------------------------------------------------------\r
463 \r
464 void HPJpeg_error (j_common_ptr cinfo)\r
465 {\r
466 \r
467 }\r
468 \r
469 extern "C"\r
470 {\r
471 void jpeg_buffer_dest (j_compress_ptr cinfo, JOCTET* outbuff, void* flush_output_buffer_callback);\r
472 void hp_rgb_ycc_setup (int iFlag);\r
473 }\r
474 \r
475 DRIVER_ERROR  QuickConnect::Compress () \r
476 {\r
477 \r
478         BYTE    *p;\r
479     int     volatile iQuality = 100;\r
480 \r
481 /*\r
482  *  Convert the byte buffer to jpg, if converted size is greater than 2MB, delete it and\r
483  *  convert with a higher compression factor.\r
484  */\r
485 \r
486     struct jpeg_compress_struct cinfo;\r
487     struct jpeg_error_mgr       jerr;\r
488     jmp_buf                     setjmp_buffer;\r
489 \r
490     BOOL    bRedo;\r
491 \r
492 //  Use the standard RGB to YCC table rather than the modified one for JetReady\r
493 \r
494     hp_rgb_ycc_setup (0);\r
495     do\r
496     {\r
497         bRedo = 0;\r
498 \r
499         m_iJpegBufferPos = 0;\r
500         memset (m_pbyJpegBuffer, 0xFF, m_iMaxFileSize);\r
501 \r
502         cinfo.err = jpeg_std_error (&jerr);\r
503         jerr.error_exit = HPJpeg_error;\r
504         if (setjmp (setjmp_buffer))\r
505         {\r
506             jpeg_destroy_compress (&cinfo);\r
507             return SYSTEM_ERROR;\r
508         }\r
509 \r
510         jpeg_create_compress (&cinfo);\r
511         cinfo.in_color_space = JCS_RGB;\r
512         jpeg_set_defaults (&cinfo);\r
513         cinfo.image_width = m_iRowWidth / 3;\r
514         cinfo.image_height = m_iRowNumber;\r
515         cinfo.input_components = 3;\r
516         cinfo.data_precision = 8;\r
517         jpeg_set_quality (&cinfo, iQuality, TRUE);\r
518         jpeg_buffer_dest (&cinfo, (JOCTET *) this, (void *) (HPFlush_output_buffer_callback));\r
519         jpeg_start_compress (&cinfo, TRUE);\r
520         JSAMPROW    pRowArray[1];\r
521         p = m_pbyInputBuffer;\r
522         for (int i = 0; i < m_iRowNumber; i++)\r
523         {\r
524             pRowArray[0] = (JSAMPROW) p;\r
525             jpeg_write_scanlines (&cinfo, pRowArray, 1);\r
526             p += (m_iRowWidth);\r
527             if (m_iJpegBufferPos > m_iMaxFileSize)\r
528             {\r
529                 m_iJpegBufferPos = 0;\r
530                 bRedo = 1;\r
531             }\r
532         }\r
533         jpeg_finish_compress (&cinfo);\r
534         jpeg_destroy_compress (&cinfo);\r
535         iQuality -= 10;\r
536         if (iQuality == 0)\r
537         {\r
538             m_iJpegBufferPos = 0;\r
539             return SYSTEM_ERROR;\r
540         }\r
541     } while (bRedo);\r
542 \r
543 #ifdef _DEBUG\r
544     char    szFileName[64];\r
545     sprintf (szFileName, "C:\\temp\\HPJpeg.jpg");\r
546     FILE    *fp = fopen (szFileName, "wb");\r
547     if (fp)\r
548     {\r
549         fwrite (m_pbyJpegBuffer, 1, m_iJpegBufferPos, fp);\r
550         fclose (fp);\r
551     }\r
552 #endif\r
553                     \r
554     return NO_ERROR;\r
555 }\r
556 \r
557 DRIVER_ERROR QuickConnect::AddExifHeader ()\r
558 {\r
559 \r
560     DRIVER_ERROR    err;\r
561     BYTE            *pBuffer = m_pbyJpegBuffer;\r
562 \r
563 /*\r
564  *  Jpeg APP2 Marker\r
565  *  APP2 Header|   Length  |         Identifier     | Version |Number of Tags\r
566  *  -------------------------------------------------------------------------\r
567  *  0xFF|0xE2  |0x00 | 0x23|0x48|0x50|0x51|0x43|0x00|0x00|0x01|0x00|0x02\r
568  *      Length = No. of Tags * length of tag + length of APP2 marker\r
569  *  -------------------------------------------------------------------------\r
570  *  Tag ID   |field Type|        Count      |Value Offset\r
571  *  -------------------------------------------------------------\r
572  *  0x00|0x01|0x00|0x03 |0x00|0x00|0x00|0x01|0x00|0x00|0x00|0x01\r
573  *  -------------------------------------------------------------\r
574  *      Field Type 0x0003 stands for short \r
575  *      Count and Value Offset are 4 bytes in TIFF convention. \r
576  *      If the count <=4, Value Offset satisfies.  If the count is bigger than 4 bytes,\r
577  *      it will be offset to where data is located. \r
578  */\r
579 \r
580     unsigned char App2[] = {"\xFF\xE2\x00\x23\x48\x50\x51\x43\x00\x00\x01\x00\x02"};\r
581     unsigned char szApp2Markers[2][12];\r
582     int     iNumTags = 0;\r
583     int     iOpts[2];\r
584 \r
585 // Things to set are: PhotoFix, RedEyeRemoval\r
586 \r
587     iOpts[0] = m_iRemoveRedEye;\r
588     iOpts[1] = m_iBorderless;\r
589 \r
590     short   skey;\r
591     unsigned char szTag[] = {"\x00\x01\x00\x03\x00\x00\x00\x01\x00\x00\x00\x01"};\r
592     unsigned int iVal;\r
593 \r
594     for (skey = 1; skey <= 2; skey++)\r
595     {\r
596         szTag[0] = (BYTE) ((skey & 0xFF) >> 8);\r
597         szTag[1] = (BYTE) (skey & 0xFF);\r
598 \r
599         iVal = iOpts[skey];\r
600         szTag[8]  = (BYTE) ((iVal >> 24) & 0xFF);\r
601         szTag[9]  = (BYTE) ((iVal >> 16) & 0xFF);\r
602         szTag[10] = (BYTE) ((iVal >>  8) & 0xFF);\r
603         szTag[11] = (BYTE) (iVal & 0xFF);\r
604         memcpy (szApp2Markers[iNumTags], szTag, 12);\r
605         iNumTags++;\r
606 \r
607         skey = (short) iNumTags * 12 + 11;\r
608         App2[2]  = (BYTE) ((skey >> 8) & 0xFF);\r
609         App2[3]  = (BYTE) (skey & 0xFF); \r
610         App2[11] = (BYTE) ((iNumTags >> 8) & 0xFF);\r
611         App2[12] = (BYTE) (iNumTags & 0xFF);\r
612     }\r
613 \r
614 /*\r
615  *  First write the SOI and JFIF header\r
616  *  File structure is:\r
617  *\r
618  *  BYTE SOI[2];          // 00h  Start of Image Marker\r
619  *  BYTE APP0[2];         // 02h  Application Use Marker\r
620  *  BYTE Length[2];       // 04h  Length of APP0 Field\r
621  *  BYTE Identifier[5];   // 06h  "JFIF" (zero terminated) Id String\r
622  *  BYTE Version[2];      // 07h  JFIF Format Revision\r
623  *  BYTE Units;           // 09h  Units used for Resolution\r
624  *  BYTE Xdensity[2];     // 0Ah  Horizontal Resolution     \r
625  *  BYTE Ydensity[2];     // 0Ch  Vertical Resolution\r
626  *  BYTE XThumbnail;      // 0Eh  Horizontal Pixel Count\r
627  *  BYTE YThumbnail;      // 0Fh  Vertical Pixel Count\r
628  */\r
629 \r
630     short sJFIFHeaderSize = ((((short) pBuffer[4]) << 8) | pBuffer[5]) + 4;\r
631     err = Send ((const BYTE *) pBuffer, sJFIFHeaderSize);\r
632     ERRCHECK;\r
633 \r
634     m_iJpegBufferPos -= sJFIFHeaderSize;\r
635     pBuffer += sJFIFHeaderSize;\r
636 \r
637     if (iNumTags != 0)\r
638     {\r
639         err = Send ((const BYTE *) App2, 13);\r
640         for (int i = 0; i < iNumTags; i++)\r
641         {\r
642             err = Send ((const BYTE *) szApp2Markers[i], 12);\r
643         }\r
644     }\r
645 \r
646         err = Send ((const BYTE *) pBuffer, m_iJpegBufferPos);\r
647     return err;\r
648 }\r
649 \r
650 APDK_END_NAMESPACE\r
651 \r
652 #endif  // defined APDK_QUICKCONNECT\r
653 \r