1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        pdfimage.cpp
3 // Purpose:     Implementation of wxPdfImage classes
4 // Author:      Ulrich Telle
5 // Created:     2005-08-11
6 // Copyright:   (c) Ulrich Telle
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 /// \file pdfimage.cpp Implementation of the wxPdfImage class
11 
12 // For compilers that support precompilation, includes <wx/wx.h>.
13 #include <wx/wxprec.h>
14 
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20 #include <wx/wx.h>
21 #endif
22 
23 #include <wx/uri.h>
24 #include <wx/url.h>
25 #include <wx/gifdecod.h>
26 #include <wx/wfstream.h>
27 #include <wx/zstream.h>
28 
29 #include "wx/pdfdocument.h"
30 #include "wx/pdfimage.h"
31 #include "wx/pdfutility.h"
32 
33 wxFileSystem* wxPdfImage::ms_fileSystem = NULL;
34 
35 wxFileSystem*
GetFileSystem()36 wxPdfImage::GetFileSystem()
37 {
38   if (ms_fileSystem == NULL)
39   {
40     static wxFileSystem fileSystem;
41     ms_fileSystem = &fileSystem;
42   }
43   return ms_fileSystem;
44 }
45 
46 // ----------------------------------------------------------------------------
47 // wxPdfImage: class representing image objects
48 // ----------------------------------------------------------------------------
49 
wxPdfImage(wxPdfDocument * document,int index,const wxString & filename,const wxString & type)50 wxPdfImage::wxPdfImage(wxPdfDocument* document, int index, const wxString& filename, const wxString& type)
51 {
52   m_document = document;
53   m_index    = index;
54   m_name     = filename;
55   m_maskImage = 0;
56   m_isFormObj = false;
57   m_fromWxImage = false;
58   m_validWxImage = false;
59 
60   m_width    = 0;
61   m_height   = 0;
62   m_cs       = wxS("");
63   m_bpc      = '\0';
64   m_f        = wxS("");
65   m_parms    = wxS("");
66 
67   m_palSize  = 0;
68   m_pal      = NULL;
69   m_trnsSize = 0;
70   m_trns     = NULL;
71   m_dataSize = 0;
72   m_data     = NULL;
73 
74   wxString fileURL = m_name;
75   wxURI uri(m_name);
76   if (!uri.HasScheme())
77   {
78     fileURL = wxFileSystem::FileNameToURL(m_name);
79   }
80   m_imageFile = wxPdfImage::GetFileSystem()->OpenFile(fileURL);
81   if (m_imageFile != NULL)
82   {
83     wxString mimeType = m_imageFile->GetMimeType();
84     m_type = (mimeType != wxEmptyString) ? mimeType : type.Lower();
85     m_imageStream = m_imageFile->GetStream();
86   }
87   else
88   {
89     m_type = type.Lower();
90     m_imageStream = NULL;
91   }
92 }
93 
wxPdfImage(wxPdfDocument * document,int index,const wxString & name,const wxImage & image,bool jpegFormat)94 wxPdfImage::wxPdfImage(wxPdfDocument* document, int index, const wxString& name, const wxImage& image,
95                        bool jpegFormat)
96 {
97   m_document = document;
98   m_index    = index;
99   m_name     = name;
100   m_maskImage = 0;
101   m_isFormObj = false;
102   m_fromWxImage = true;
103 
104   m_width    = 0;
105   m_height   = 0;
106   m_cs       = wxS("");
107   m_bpc      = '\0';
108   m_f        = wxS("");
109   m_parms    = wxS("");
110 
111   m_palSize  = 0;
112   m_pal      = NULL;
113   m_trnsSize = 0;
114   m_trns     = NULL;
115   m_dataSize = 0;
116   m_data     = NULL;
117 
118   m_validWxImage = ConvertWxImage(image, jpegFormat);
119 
120   m_imageFile = NULL;
121   m_imageStream = NULL;
122 }
123 
wxPdfImage(wxPdfDocument * document,int index,const wxString & name,wxInputStream & stream,const wxString & mimeType)124 wxPdfImage::wxPdfImage(wxPdfDocument* document, int index, const wxString& name, wxInputStream& stream, const wxString& mimeType)
125 {
126   m_document = document;
127   m_index    = index;
128   m_name     = name;
129   m_maskImage = 0;
130   m_isFormObj = false;
131   m_fromWxImage = false;
132   m_validWxImage = false;
133 
134   m_width    = 0;
135   m_height   = 0;
136   m_cs       = wxS("");
137   m_bpc      = '\0';
138   m_f        = wxS("");
139   m_parms    = wxS("");
140 
141   m_palSize  = 0;
142   m_pal      = NULL;
143   m_trnsSize = 0;
144   m_trns     = NULL;
145   m_dataSize = 0;
146   m_data     = NULL;
147 
148   m_imageFile = NULL;
149   m_type = mimeType;
150   m_imageStream = &stream;
151 }
152 
~wxPdfImage()153 wxPdfImage::~wxPdfImage()
154 {
155   if (m_pal  != NULL) delete [] m_pal;
156   if (m_trns != NULL) delete [] m_trns;
157   if (m_data != NULL) delete [] m_data;
158 }
159 
160 bool
ConvertWxImage(const wxImage & image,bool jpegFormat)161 wxPdfImage::ConvertWxImage(const wxImage& image, bool jpegFormat)
162 {
163 #if !wxUSE_LIBJPEG
164   if (jpegFormat)
165   {
166     return false;
167   }
168 #endif // wxUSE_LIBJPEG
169 
170   bool isValid = false;
171   wxBitmapType bitmapType = (jpegFormat) ? wxBITMAP_TYPE_JPEG : wxBITMAP_TYPE_PNG;
172   if (wxImage::FindHandler(bitmapType) == NULL)
173   {
174 #if wxUSE_LIBJPEG
175     if (jpegFormat)
176     {
177       wxImage::AddHandler(new wxJPEGHandler());
178     }
179     else
180 #endif // wxUSE_LIBJPEG
181     {
182       wxImage::AddHandler(new wxPNGHandler());
183     }
184   }
185   wxMemoryOutputStream os;
186   isValid = image.SaveFile(os, bitmapType);
187   if (isValid)
188   {
189     wxMemoryInputStream is(os);
190 #if wxUSE_LIBJPEG
191     if (jpegFormat)
192     {
193       m_type = wxS("jpeg");
194       isValid = ParseJPG(&is);
195     }
196     else
197 #endif // wxUSE_LIBJPEG
198     {
199       m_type = wxS("png");
200       isValid = ParsePNG(&is);
201     }
202   }
203   return isValid;
204 }
205 
206 bool
Parse()207 wxPdfImage::Parse()
208 {
209   // Check whether this image originated from an wxImage and is valid
210   if (m_fromWxImage) return m_validWxImage;
211 
212   bool isValid = false;
213 
214   if (m_imageStream)
215   {
216     if ((m_type.StartsWith(wxS("image/")) && m_type.EndsWith(wxS("png"))) ||
217         m_type == wxS("png"))
218     {
219       isValid = ParsePNG(m_imageStream);
220     }
221     else if ((m_type.StartsWith(wxS("image/")) && m_type.EndsWith(wxS("jpeg"))) ||
222              m_type == wxS("jpeg") || m_type == wxS("jpg"))
223     {
224       isValid = ParseJPG(m_imageStream);
225     }
226 #if wxUSE_GIF
227     else if ((m_type.StartsWith(wxS("image/")) && m_type.EndsWith(wxS("gif"))) ||
228              m_type == wxS("gif"))
229     {
230       isValid = ParseGIF(m_imageStream);
231     }
232 #endif // wxUSE_GIF
233     else
234     {
235       if ((m_type.StartsWith(wxS("image/")) && m_type.EndsWith(wxS("wmf"))) ||
236           m_type == wxS("wmf") || m_name.Right(2) == wxS(".wmf"))
237       {
238         m_isFormObj = true;
239         isValid = ParseWMF(m_imageStream);
240       }
241     }
242     if (m_imageFile != NULL)
243     {
244       delete m_imageFile;
245       m_imageFile = NULL;
246     }
247   }
248   return isValid;
249 }
250 
251 // --- Parse PNG image file ---
252 
253 bool
ParsePNG(wxInputStream * imageStream)254 wxPdfImage::ParsePNG(wxInputStream* imageStream)
255 {
256   bool isValid = false;
257 
258   // Check signature
259   char buffer[8];
260   imageStream->Read(buffer,8);
261   if (strncmp(buffer,"\x89PNG\x0D\x0A\x1A\x0A",8) != 0)
262   {
263     // Not a PNG file
264     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
265                wxString::Format(_("'%s' not a PNG file."), m_name.c_str()));
266     return false;
267   }
268 
269   // Read header chunk
270   imageStream->Read(buffer,4);
271   imageStream->Read(buffer,4);
272   if (strncmp(buffer,"IHDR",4) != 0)
273   {
274     // Incorrect PNG file
275     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
276                wxString::Format(_("Incorrect PNG file '%s'."), m_name.c_str()));
277     return false;
278   }
279 
280   int w = ReadIntBE(imageStream);
281   int h = ReadIntBE(imageStream);
282 
283   imageStream->Read(buffer,1);
284   char bpc = buffer[0];
285   if (bpc > 8)
286   {
287     // 16-bit depth not supported
288     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
289                wxString::Format(_("16-bit depth not supported: '%s'."), m_name.c_str()));
290     return false;
291   }
292 
293   wxString colspace = wxEmptyString;
294   imageStream->Read(buffer,1);
295   char ct = buffer[0];
296   if (ct == 0)
297   {
298     colspace = wxS("DeviceGray");
299   }
300   else if (ct == 2)
301   {
302     colspace = wxS("DeviceRGB");
303   }
304   else if (ct == 3)
305   {
306     colspace = wxS("Indexed");
307   }
308   else
309   {
310     // Unknown colour type
311     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
312                wxString::Format(_("Unknown colour type: '%s'."), m_name.c_str()));
313     return false;
314   }
315 
316   imageStream->Read(buffer,3);
317   if (buffer[0] != 0)
318   {
319     // Unknown compression method
320     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
321                wxString::Format(_("Unknown compression method: '%s'."), m_name.c_str()));
322     return false;
323   }
324   if (buffer[1] != 0)
325   {
326     // Unknown filter method
327     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
328                wxString::Format(_("Unknown filter method: '%s'."), m_name.c_str()));
329     return false;
330   }
331   if (buffer[2] != 0)
332   {
333     // Interlacing not supported
334     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
335                wxString::Format(_("Interlacing not supported: '%s'."), m_name.c_str()));
336     return false;
337   }
338 
339   imageStream->Read(buffer,4);
340   m_parms = wxString::Format(wxS("/DecodeParms <</Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d>>"), (ct==2 ? 3 : 1), (int) bpc, w);
341 
342   // Scan chunks looking for palette, transparency and image data
343   m_palSize  = 0;
344   m_pal      = NULL;
345   m_trnsSize = 0;
346   m_trns     = NULL;
347   m_dataSize = 0;
348   m_data     = NULL;
349   int n;
350   do
351   {
352     n = ReadIntBE(imageStream);
353     imageStream->Read(buffer,4);
354     if (strncmp(buffer,"PLTE",4) == 0)
355     {
356       // Read palette
357       m_palSize = n;
358       m_pal = new char[n];
359       imageStream->Read(m_pal,n);
360       imageStream->Read(buffer,4);
361     }
362     else if (strncmp(buffer,"tRNS",4) == 0)
363     {
364       // Read transparency info
365       char* t = new char[n];
366       imageStream->Read(t,n);
367       if (ct == 0)
368       {
369         m_trnsSize = 1;
370         m_trns = new char[1];
371         m_trns[0] = t[1];
372       }
373       else if (ct == 2)
374       {
375         m_trnsSize = 3;
376         m_trns = new char[3];
377         m_trns[0] = t[1];
378         m_trns[1] = t[3];
379         m_trns[2] = t[5];
380       }
381       else
382       {
383         int pos;
384         for (pos = 0; (pos < n) && (t[pos] != 0); pos++)
385         {
386         }
387         if (pos < n)
388         {
389           m_trnsSize = 1;
390           m_trns = new char[1];
391           m_trns[0] = pos;
392         }
393       }
394       imageStream->Read(buffer,4);
395       delete [] t;
396     }
397     else if (strncmp(buffer,"IDAT",4) == 0)
398     {
399       // Read image data block
400       int prevSize = m_dataSize;
401       char* prevData = m_data;
402       m_dataSize += n;
403       m_data = new char[m_dataSize];
404       if (prevSize > 0)
405       {
406         memcpy(m_data, prevData, prevSize);
407         delete [] prevData;
408       }
409       imageStream->Read(m_data+prevSize,n);
410       imageStream->Read(buffer,4);
411     }
412     else if (strncmp(buffer,"IEND",4) == 0)
413     {
414       break;
415     }
416     else
417     {
418       char* temp = new char[n];
419       imageStream->Read(temp,n);
420       delete [] temp;
421       imageStream->Read(buffer,4);
422     }
423   }
424   while (n);
425 
426   if (colspace == wxS("Indexed") && m_pal == NULL)
427   {
428     if (m_pal  != NULL) delete [] m_pal;
429     if (m_trns != NULL) delete [] m_trns;
430     if (m_data != NULL) delete [] m_data;
431     // Missing palette
432     wxLogDebug(wxString(wxS("wxPdfImage::ParsePNG: ")) +
433                wxString::Format(_("Missing palette: '%s'."), m_name.c_str()));
434     return false;
435   }
436 
437   m_width  = w;
438   m_height = h;
439   m_cs     = colspace;
440   m_bpc    = bpc;
441   m_f      = wxS("FlateDecode");
442 
443   isValid = true;
444   return isValid;
445 }
446 
447 //--- Parse JPEG image file
448 
449 // some defines for the different JPEG block types
450 
451 #define M_SOF0  0xC0     // Start Of Frame N
452 #define M_SOF1  0xC1     // N indicates which compression process
453 #define M_SOF2  0xC2     // Only SOF0-SOF2 are now in common use
454 #define M_SOF3  0xC3
455 #define M_SOF5  0xC5     // NB: codes C4 and CC are NOT SOF markers
456 #define M_SOF6  0xC6
457 #define M_SOF7  0xC7
458 #define M_SOF9  0xC9
459 #define M_SOF10 0xCA
460 #define M_SOF11 0xCB
461 #define M_SOF13 0xCD
462 #define M_SOF14 0xCE
463 #define M_SOF15 0xCF
464 #define M_SOI   0xD8
465 #define M_EOI   0xD9     // End Of Image (end of datastream)
466 #define M_SOS   0xDA     // Start Of Scan (begins compressed data)
467 #define M_COM   0xFE     // COMment
468 
469 #define M_PSEUDO 0xFFD8  // pseudo marker for start of image(byte 0)
470 
471 bool
ParseJPG(wxInputStream * imageStream)472 wxPdfImage::ParseJPG(wxInputStream* imageStream)
473 {
474   bool isValid = false;
475   wxString colspace = wxS("");
476 
477   m_palSize  = 0;
478   m_pal      = NULL;
479   m_trnsSize = 0;
480   m_trns     = NULL;
481   m_dataSize = 0;
482   m_data     = NULL;
483 
484   unsigned char buffer[3];
485   imageStream->Read(buffer,3);
486   if (strncmp((const char*) buffer,"\xff\xd8\xff",3) != 0)
487   {
488     // Not a JPEG file
489     wxLogDebug(wxString(wxS("wxPdfImage::ParseJPG: ")) +
490                wxString::Format(_("'%s' not a JPEG file."), m_name.c_str()));
491     return false;
492   }
493 
494   // Extract info from a JPEG file
495   unsigned int   marker = M_PSEUDO;
496   unsigned short ffRead = 1;
497   unsigned char  bits     = 0;
498   unsigned short height   = 0;
499   unsigned short width    = 0;
500   unsigned char  channels = 0;
501 
502 
503   bool ready = false;
504   int lastMarker;
505   int commentCorrection;
506   int a;
507   while (!ready)
508   {
509     lastMarker = marker;
510     commentCorrection = 1;
511     a = 0;
512 
513     // get marker byte, swallowing possible padding
514     if (lastMarker == M_COM && commentCorrection)
515     {
516       // some software does not count the length bytes of COM section
517       // one company doing so is very much envolved in JPEG... so we accept too
518       // by the way: some of those companies changed their code now...
519       commentCorrection = 2;
520     }
521     else
522     {
523       lastMarker = 0;
524       commentCorrection = 0;
525     }
526     if (ffRead)
527     {
528       a = 1; // already read 0xff in filetype detection
529     }
530     do
531     {
532       imageStream->Read(buffer,1);
533       if (imageStream->Eof())
534       {
535         marker = M_EOI; // we hit EOF
536         break;
537       }
538       marker = buffer[0];
539       if (lastMarker == M_COM && commentCorrection > 0)
540       {
541         if (marker != 0xFF)
542         {
543           marker = 0xff;
544           commentCorrection--;
545         }
546         else
547         {
548           lastMarker = M_PSEUDO; // stop skipping non 0xff for M_COM
549         }
550       }
551       if (++a > 10)
552       {
553         // who knows the maxim amount of 0xff? though 7
554         // but found other implementations
555         marker = M_EOI;
556         break;
557       }
558     }
559     while (marker == 0xff);
560 
561     if (a < 2)
562     {
563       marker = M_EOI; // at least one 0xff is needed before marker code
564     }
565     if (lastMarker == M_COM && commentCorrection)
566     {
567       marker = M_EOI; // ah illegal: char after COM section not 0xFF
568     }
569 
570     ffRead = 0;
571     switch (marker)
572     {
573       case M_SOF0:
574       case M_SOF1:
575       case M_SOF2:
576       case M_SOF3:
577       case M_SOF5:
578       case M_SOF6:
579       case M_SOF7:
580       case M_SOF9:
581       case M_SOF10:
582       case M_SOF11:
583       case M_SOF13:
584       case M_SOF14:
585       case M_SOF15:
586         // handle SOFn block
587         imageStream->SeekI(2, wxFromCurrent); // skip 2-byte length field
588         imageStream->Read(&bits,1);
589         height = ReadUShortBE(imageStream);
590         width  = ReadUShortBE(imageStream);
591         imageStream->Read(&channels,1);
592         isValid = true;
593         ready = true;
594         break;
595 
596       case M_SOS:
597       case M_EOI:
598         isValid = false;
599         ready = true;
600 
601       default:
602         {
603           // anything else isn't interesting
604           off_t pos = (unsigned int) ReadUShortBE(imageStream);
605           pos = pos-2;
606           if (pos)
607           {
608             imageStream->SeekI(pos, wxFromCurrent);
609           }
610         }
611         break;
612     }
613   }
614 
615   if (isValid)
616   {
617     if (channels == 3)
618     {
619       colspace = wxS("DeviceRGB");
620     }
621     else if(channels == 4)
622     {
623       colspace = wxS("DeviceCMYK");
624     }
625     else
626     {
627       colspace = wxS("DeviceGray");
628     }
629     m_bpc = bits;
630 
631     //Read whole file
632     imageStream->SeekI(0);
633     m_dataSize = imageStream->GetLength();
634     m_data = new char[m_dataSize];
635     imageStream->Read(m_data,m_dataSize);
636 
637     m_width  = width;
638     m_height = height;
639     m_cs     = colspace;
640     m_bpc    = bits;
641     m_f      = wxS("DCTDecode");
642   }
643 
644   return isValid;
645 }
646 
647 // --- Parse GIF image file ---
648 
649 bool
ParseGIF(wxInputStream * imageStream)650 wxPdfImage::ParseGIF(wxInputStream* imageStream)
651 {
652 #if wxUSE_GIF
653   bool isValid = false;
654 
655   m_palSize  = 0;
656   m_pal      = NULL;
657   m_trnsSize = 0;
658   m_trns     = NULL;
659   m_dataSize = 0;
660   m_data     = NULL;
661 
662 #if wxCHECK_VERSION(2,7,1)
663   wxGIFDecoder gif;
664   if (!gif.CanRead(*imageStream))
665   {
666     wxLogDebug(wxString(wxS("wxPdfImage::ParseGIF: ")) +
667                wxString::Format(_("'%s' not a GIF file."), m_name.c_str()));
668     return false;
669   }
670 
671   if (gif.LoadGIF(*imageStream) != wxGIF_OK)
672   {
673     wxLogDebug(wxString(wxS("wxPdfImage::ParseGIF: ")) +
674                wxString::Format(_("Invalid GIF file '%s'."), m_name.c_str()));
675     return false;
676   }
677 
678   isValid = true;
679   wxSize gifSize = gif.GetFrameSize(0);
680   m_width = gifSize.GetWidth();
681   m_height = gifSize.GetHeight();
682   m_cs = wxS("Indexed");
683   m_bpc    = 8;
684 
685   m_palSize  = 768;
686   m_pal = new char[m_palSize];
687   memcpy(m_pal,gif.GetPalette(0),m_palSize);
688 
689   int trns = gif.GetTransparentColourIndex(0);
690   if (trns != -1)
691   {
692     m_trnsSize = 3;
693     m_trns     = new char[3];
694     m_trns[0] = m_pal[3*trns + 0];
695     m_trns[1] = m_pal[3*trns + 1];
696     m_trns[2] = m_pal[3*trns + 2];
697   }
698 
699   m_dataSize = m_width * m_height;
700   if (m_document->m_compress)
701   {
702     m_f = wxS("FlateDecode");
703     wxMemoryOutputStream* p = new wxMemoryOutputStream();
704     wxZlibOutputStream q(*p);
705     q.Write(gif.GetData(0),m_dataSize);
706     q.Close();
707     m_dataSize = p->TellO();
708     m_data = new char[m_dataSize];
709     p->CopyTo(m_data,m_dataSize);
710     delete p;
711   }
712   else
713   {
714     m_f = wxS("");
715     m_data = new char[m_dataSize];
716     memcpy(m_data,gif.GetData(0),m_dataSize);
717   }
718 #else
719   wxGIFDecoder gif(imageStream);
720   if (!gif.CanRead())
721   {
722     wxLogDebug(wxString(wxS("wxPdfImage::ParseGIF: ")) +
723                wxString::Format(_("'%s' not a GIF file."), m_name.c_str()));
724     return false;
725   }
726 
727   if (gif.ReadGIF() != wxGIF_OK)
728   {
729     wxLogDebug(wxString(wxS("wxPdfImage::ParseGIF: ")) +
730                wxString::Format(_("Invalid GIF file '%s'."), m_name.c_str()));
731     return false;
732   }
733 
734   isValid = true;
735   m_width = gif.GetWidth();
736   m_height = gif.GetHeight();
737   m_cs = wxS("Indexed");
738   m_bpc    = 8;
739 
740   m_palSize  = 768;
741   m_pal = new char[m_palSize];
742   memcpy(m_pal,gif.GetPalette(),m_palSize);
743 
744   int trns = gif.GetTransparentColour();
745   if (trns != -1)
746   {
747     m_trnsSize = 3;
748     m_trns     = new char[3];
749     m_trns[0] = m_pal[3*trns + 0];
750     m_trns[1] = m_pal[3*trns + 1];
751     m_trns[2] = m_pal[3*trns + 2];
752   }
753 
754   m_dataSize = m_width * m_height;
755   if (m_document->m_compress)
756   {
757     m_f = wxS("FlateDecode");
758     wxMemoryOutputStream* p = new wxMemoryOutputStream();
759     wxZlibOutputStream q(*p);
760     q.Write(gif.GetData(),m_dataSize);
761     q.Close();
762     m_dataSize = p->TellO();
763     m_data = new char[m_dataSize];
764     p->CopyTo(m_data,m_dataSize);
765     delete p;
766   }
767   else
768   {
769     m_f = wxS("");
770     m_data = new char[m_dataSize];
771     memcpy(m_data,gif.GetData(),m_dataSize);
772   }
773 #endif
774   return isValid;
775 #else // !wxUSE_GIF
776   return false;
777 #endif // wxUSE_GIF/!wxUSE_GIF
778 }
779 
780 // --- Parse WMF image file ---
781 
782 /// Class representing GDI objects while parsing WMF files. (For internal use only)
783 class GdiObject
784 {
785 public:
786   char           type;
787   short          style;
788   unsigned char  r;
789   unsigned char  g;
790   unsigned char  b;
791   unsigned char  a;
792   unsigned short hatch;
793   double         width;
794 };
795 
796 static void
AddGdiObject(wxArrayPtrVoid & gdiObjects,void * obj)797 AddGdiObject(wxArrayPtrVoid& gdiObjects, void* obj)
798 {
799   // find next available slot
800   size_t idx;
801   size_t n = gdiObjects.GetCount();
802   for (idx = 0; idx < n; idx++)
803   {
804     if (gdiObjects[idx] == NULL) break;
805   }
806   if (idx < n)
807   {
808     gdiObjects[idx] = obj;
809   }
810   else
811   {
812     gdiObjects.Add(obj);
813   }
814 }
815 
816 bool
ParseWMF(wxInputStream * imageStream)817 wxPdfImage::ParseWMF(wxInputStream* imageStream)
818 {
819   bool isValid = false;
820   char buffer[64];
821 
822   wxArrayPtrVoid gdiObjects;
823 
824   // check for Aldus placeable metafile header
825   unsigned int key = ReadIntLE(imageStream);
826   int headSize = 18 - 4; // WMF header minus four bytes already read
827   if (key == 0x9AC6CDD7)
828   {
829     headSize += 22; // Aldus header
830   }
831 
832   // strip headers
833   imageStream->Read(buffer, headSize);
834 
835   // define some state variables
836   short polyFillMode = 0;
837   bool nullPen = false;
838   bool nullBrush = false;
839   bool endRecord = false;
840 
841   wxString data = wxEmptyString;
842   wxString op;
843   // read the records
844   unsigned int size;
845   unsigned short func;
846   unsigned short idx;
847   short wo[2] = { 0, 0 };
848   short we[2] = { 0, 0 };
849   short dashArray[8];
850   size_t lenDashArray;
851   size_t i;
852   short j, k, px, py;
853   GdiObject* obj = NULL;
854   while (!imageStream->Eof() && !endRecord)
855   {
856     // size of record given in WORDs (= 2 bytes)
857     size = ReadUIntLE(imageStream);
858     // func is number of GDI function
859     func = ReadUShortLE(imageStream);
860 
861     // parameters are read and processed
862     // as necessary by the case statement below.
863     // NB. parameters to GDI functions are stored in reverse order
864     // however structures are not reversed,
865     // e.g. POINT { int x, int y } where x=3000 (0x0BB8) and y=-1200 (0xFB50)
866     // is stored as B8 0B 50 FB
867 
868     // process each record.
869     // function numbers are defined in wingdi.h
870     switch (func)
871     {
872       case 0x020b:  // SetWindowOrg
873         // do not allow window origin to be changed
874         // after drawing has begun
875         if (data.Length() == 0)
876         {
877           wo[1] = ReadShortLE(imageStream);
878           wo[0] = ReadShortLE(imageStream);
879         }
880         break;
881 
882       case 0x020c:  // SetWindowExt
883         // do not allow window extent to be changed
884         // after drawing has begun
885         if (data.Length() == 0)
886         {
887           we[1] = ReadShortLE(imageStream);
888           we[0] = ReadShortLE(imageStream);
889         }
890         break;
891 
892       case 0x02fc:  // CreateBrushIndirect
893         {
894           GdiObject* brush = new GdiObject();
895           brush->style = ReadShortLE(imageStream);
896           imageStream->Read(&brush->r, 1);
897           imageStream->Read(&brush->g, 1);
898           imageStream->Read(&brush->b, 1);
899           imageStream->Read(&brush->a, 1);
900           brush->hatch = ReadUShortLE(imageStream);
901           brush->type = 'B';
902           AddGdiObject(gdiObjects, brush);
903         }
904         break;
905 
906       case 0x02fa:  // CreatePenIndirect
907         {
908           GdiObject* pen = new GdiObject();
909           pen->style = ReadShortLE(imageStream);
910           short width = ReadShortLE(imageStream);
911           /* short dummy = */ ReadShortLE(imageStream);
912           imageStream->Read(&pen->r, 1);
913           imageStream->Read(&pen->g, 1);
914           imageStream->Read(&pen->b, 1);
915           imageStream->Read(&pen->a, 1);
916 
917           // convert width from twips to user unit
918           pen->width = width / (20 * m_document->m_k);
919           pen->type = 'P';
920           AddGdiObject(gdiObjects, pen);
921         }
922         break;
923 
924       // MUST create other GDI objects even if we don't handle them
925       // otherwise object numbering will get out of sequence
926       case 0x06fe: // CreateBitmap
927       case 0x02fd: // CreateBitmapIndirect
928       case 0x00f8: // CreateBrush
929       case 0x02fb: // CreateFontIndirect
930       case 0x00f7: // CreatePalette
931       case 0x01f9: // CreatePatternBrush
932       case 0x06ff: // CreateRegion
933       case 0x0142: // DibCreatePatternBrush
934         {
935           GdiObject* dummy = new GdiObject();
936           dummy->type = 'D';
937           AddGdiObject(gdiObjects, dummy);
938         }
939         break;
940 
941       case 0x0106:  // SetPolyFillMode
942         polyFillMode = ReadShortLE(imageStream);
943         break;
944 
945       case 0x01f0:  // DeleteObject
946         {
947           idx = ReadUShortLE(imageStream);
948           delete ((GdiObject*) gdiObjects[idx]);
949           gdiObjects[idx] = NULL;
950         }
951         break;
952 
953       case 0x012d:  // SelectObject
954         {
955           idx = ReadUShortLE(imageStream);
956           obj = (GdiObject*) gdiObjects[idx];
957 
958           switch (obj->type)
959           {
960             case 'B':
961               nullBrush = false;
962 
963               if (obj->style == 1) // BS_NULL, BS_HOLLOW
964               {
965                 nullBrush = true;
966               }
967               else
968               {
969                 data += wxPdfUtility::Double2String(obj->r/255.,3) + wxString(wxS(" "));
970                 data += wxPdfUtility::Double2String(obj->g/255.,3) + wxString(wxS(" "));
971                 data += wxPdfUtility::Double2String(obj->b/255.,3) + wxString(wxS(" rg\n"));
972               }
973               break;
974 
975             case 'P':
976               nullPen = false;
977               lenDashArray = 0;
978 
979               // dash parameters are my own - feel free to change them
980               switch (obj->style)
981               {
982                 case 0: // PS_SOLID
983                   break;
984                 case 1: // PS_DASH
985                   dashArray[0] = 3;
986                   dashArray[1] = 1;
987                   lenDashArray = 2;
988                   break;
989                 case 2: // PS_DOT
990                   dashArray[0] = 0;
991                   dashArray[1] = 5;
992                   dashArray[2] = 0;
993                   dashArray[3] = 5;
994                   lenDashArray = 4;
995                   break;
996                 case 3: // PS_DASHDOT
997                   dashArray[0] = 2;
998                   dashArray[1] = 1;
999                   dashArray[2] = 0;
1000                   dashArray[3] = 5;
1001                   dashArray[4] = 1;
1002                   lenDashArray = 5;
1003                   break;
1004                 case 4: // PS_DASHDOTDOT
1005                   dashArray[0] = 2;
1006                   dashArray[1] = 1;
1007                   dashArray[2] = 0;
1008                   dashArray[3] = 5;
1009                   dashArray[4] = 1;
1010                   dashArray[5] = 0;
1011                   dashArray[6] = 5;
1012                   dashArray[7] = 1;
1013                   lenDashArray = 8;
1014                   break;
1015                 case 5: // PS_NULL
1016                   nullPen = true;
1017                   break;
1018               }
1019 
1020               if (!nullPen)
1021               {
1022                 data += wxPdfUtility::Double2String(obj->r/255.,3) + wxString(wxS(" "));
1023                 data += wxPdfUtility::Double2String(obj->g/255.,3) + wxString(wxS(" "));
1024                 data += wxPdfUtility::Double2String(obj->b/255.,3) + wxString(wxS(" RG\n"));
1025 
1026                 data += wxPdfUtility::Double2String(obj->width*m_document->m_k,2) + wxString(wxS(" w\n"));
1027               }
1028 
1029               if (lenDashArray > 0)
1030               {
1031                 wxString s = wxS("[");
1032                 for (i = 0; i < lenDashArray; i++)
1033                 {
1034                   s += wxPdfUtility::Double2String(dashArray[i] * m_document->m_k,4);
1035                   if (i != lenDashArray-1)
1036                   {
1037                     s += wxS(" ");
1038                   }
1039                 }
1040                 s += wxS("] 0 d\n");
1041                 data += s;
1042               }
1043               break;
1044           }
1045         }
1046         break;
1047 
1048       case 0x0325: // Polyline
1049       case 0x0324: // Polygon
1050         {
1051           short* coords = new short[size-3];
1052           for (i = 0; i < size-3; i++)
1053           {
1054             coords[i] = ReadShortLE(imageStream);
1055           }
1056           short numpoints = coords[0];
1057 
1058           for (k = numpoints; k > 0; k--)
1059           {
1060             px = coords[2*k-1];
1061             py = coords[2*k];
1062 
1063             if (k < numpoints)
1064             {
1065               data += wxString::Format(wxS("%d %d l\n"), (int) px, (int) py);
1066             }
1067             else
1068             {
1069               data += wxString::Format(wxS("%d %d m\n"), (int) px, (int) py);
1070             }
1071           }
1072 
1073           if (func == 0x0325)
1074           {
1075             op = wxS("s");
1076           }
1077           else if (func == 0x0324)
1078           {
1079             if (nullPen)
1080             {
1081               if (nullBrush)
1082               {
1083                 op = wxS("n");  // no op
1084               }
1085               else
1086               {
1087                 op = wxS("f");  // fill
1088               }
1089             }
1090             else
1091             {
1092               if (nullBrush)
1093               {
1094                 op = wxS("s");  // stroke
1095               }
1096               else
1097               {
1098                 op = wxS("b");  // stroke and fill
1099               }
1100             }
1101 
1102             if (polyFillMode == 1 && (op == wxS("b") || op == wxS("f")))
1103             {
1104               op += wxS("*");  // use even-odd fill rule
1105             }
1106           }
1107           data += op + wxS("\n");
1108           delete [] coords;
1109         }
1110         break;
1111 
1112       case 0x0538: // PolyPolygon
1113         {
1114           short* coords = new short[size-3];
1115           for (i = 0; i < size-3; i++)
1116           {
1117             coords[i] = ReadShortLE(imageStream);
1118           }
1119           short numpolygons = coords[0];
1120 
1121           short adjustment = numpolygons;
1122 
1123           for (j = 1; j <= numpolygons; j++)
1124           {
1125             short numpoints = coords[j + 1];
1126 
1127             for (k = numpoints; k > 0; k--)
1128             {
1129               px = coords[2*k-1 + adjustment];
1130               py = coords[2*k   + adjustment];
1131 
1132               if (k == numpoints)
1133               {
1134                 data += wxString::Format(wxS("%d %d m\n"), (int) px, (int) py);
1135               }
1136               else
1137               {
1138                 data += wxString::Format(wxS("%d %d m\n"), (int) px, (int) py);
1139               }
1140             }
1141 
1142             adjustment += numpoints * 2;
1143           }
1144 
1145           if (nullPen)
1146           {
1147             if (nullBrush)
1148             {
1149               op = wxS("n");  // no op
1150             }
1151             else
1152             {
1153               op = wxS("f");  // fill
1154             }
1155           }
1156           else
1157           {
1158             if (nullBrush)
1159             {
1160               op = wxS("s");  // stroke
1161             }
1162             else
1163             {
1164               op = wxS("b");  // stroke and fill
1165             }
1166           }
1167 
1168           if (polyFillMode == 1 && (op == wxS("b") || op == wxS("f")))
1169           {
1170             op += wxS("*");  // use even-odd fill rule
1171           }
1172 
1173           data += op + wxS("\n");
1174           delete [] coords;
1175         }
1176         break;
1177 
1178       case 0x0000:
1179         endRecord = true;
1180         isValid = true;
1181         break;
1182       default:
1183         if (size > 3)
1184         {
1185           imageStream->SeekI(2*(size-3), wxFromCurrent);
1186         }
1187         break;
1188     }
1189   }
1190 
1191   for (i = 0; i < gdiObjects.GetCount(); i++)
1192   {
1193     if (gdiObjects[i] != NULL)
1194     {
1195       delete ((GdiObject*) gdiObjects[i]);
1196     }
1197   }
1198   m_x = wo[0];
1199   m_y = wo[1];
1200   m_width = we[0];
1201   m_height = we[1];
1202 
1203   wxCharBuffer wcb(data.ToAscii());
1204   m_dataSize = (unsigned int) data.Length();
1205   m_data = new char[m_dataSize];
1206   memcpy(m_data, (const char*) wcb, m_dataSize);
1207   return isValid;
1208 }
1209 
1210 int
ReadIntBE(wxInputStream * imageStream)1211 wxPdfImage::ReadIntBE(wxInputStream* imageStream)
1212 {
1213   // Read a 4-byte integer from file (big endian)
1214   int i32;
1215   imageStream->Read(&i32, 4);
1216   return wxINT32_SWAP_ON_LE(i32);
1217 }
1218 
1219 int
ReadIntLE(wxInputStream * imageStream)1220 wxPdfImage::ReadIntLE(wxInputStream* imageStream)
1221 {
1222   // Read a 4-byte integer from file (little endian)
1223   int i32;
1224   imageStream->Read(&i32, 4);
1225   return wxINT32_SWAP_ON_BE(i32);
1226 }
1227 
1228 unsigned int
ReadUIntBE(wxInputStream * imageStream)1229 wxPdfImage::ReadUIntBE(wxInputStream* imageStream)
1230 {
1231   // Read a unsigned 4-byte integer from file (big endian)
1232   unsigned int i32;
1233   imageStream->Read(&i32, 4);
1234   return wxUINT32_SWAP_ON_LE(i32);
1235 }
1236 
1237 unsigned int
ReadUIntLE(wxInputStream * imageStream)1238 wxPdfImage::ReadUIntLE(wxInputStream* imageStream)
1239 {
1240   // Read a unsigned 4-byte integer from file (little endian)
1241   unsigned int i32;
1242   imageStream->Read(&i32, 4);
1243   return wxUINT32_SWAP_ON_BE(i32);
1244 }
1245 
1246 short
ReadShortBE(wxInputStream * imageStream)1247 wxPdfImage::ReadShortBE(wxInputStream* imageStream)
1248 {
1249   // Read a 2-byte integer from file (big endian)
1250   short i16;
1251   imageStream->Read(&i16, 2);
1252   return wxINT16_SWAP_ON_LE(i16);
1253 }
1254 
1255 short
ReadShortLE(wxInputStream * imageStream)1256 wxPdfImage::ReadShortLE(wxInputStream* imageStream)
1257 {
1258   // Read a 2-byte integer from file (little endian)
1259   short i16;
1260   imageStream->Read(&i16, 2);
1261   return wxINT16_SWAP_ON_BE(i16);
1262 }
1263 
1264 unsigned short
ReadUShortBE(wxInputStream * imageStream)1265 wxPdfImage::ReadUShortBE(wxInputStream* imageStream)
1266 {
1267   // Read a unsigned 2-byte integer from file (big endian)
1268   unsigned short i16;
1269   imageStream->Read(&i16, 2);
1270   return wxUINT16_SWAP_ON_LE(i16);
1271 }
1272 
1273 unsigned short
ReadUShortLE(wxInputStream * imageStream)1274 wxPdfImage::ReadUShortLE(wxInputStream* imageStream)
1275 {
1276   // Read a unsigned 2-byte integer from file (little endian)
1277   unsigned short i16;
1278   imageStream->Read(&i16, 2);
1279   return wxUINT16_SWAP_ON_BE(i16);
1280 }
1281