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