wpg.c revision 018f07f7333b25743d0afff892450cebdb905c1a
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                            W   W  PPPP    GGGG                              %
6%                            W   W  P   P  G                                  %
7%                            W W W  PPPP   G GGG                              %
8%                            WW WW  P      G   G                              %
9%                            W   W  P       GGG                               %
10%                                                                             %
11%                                                                             %
12%                       Read WordPerfect Image Format                         %
13%                                                                             %
14%                              Software Design                                %
15%                              Jaroslav Fojtik                                %
16%                                 June 2000                                   %
17%                                                                             %
18%                                                                             %
19%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
20%  dedicated to making software imaging solutions freely available.           %
21%                                                                             %
22%  You may not use this file except in compliance with the License.  You may  %
23%  obtain a copy of the License at                                            %
24%                                                                             %
25%    http://www.imagemagick.org/script/license.php                            %
26%                                                                             %
27%  Unless required by applicable law or agreed to in writing, software        %
28%  distributed under the License is distributed on an "AS IS" BASIS,          %
29%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
30%  See the License for the specific language governing permissions and        %
31%  limitations under the License.                                             %
32%                                                                             %
33%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34%
35%
36*/
37
38/*
39  Include declarations.
40*/
41#include "MagickCore/studio.h"
42#include "MagickCore/blob.h"
43#include "MagickCore/blob-private.h"
44#include "MagickCore/color-private.h"
45#include "MagickCore/colormap.h"
46#include "MagickCore/colormap-private.h"
47#include "MagickCore/constitute.h"
48#include "MagickCore/exception.h"
49#include "MagickCore/exception-private.h"
50#include "MagickCore/cache.h"
51#include "MagickCore/image.h"
52#include "MagickCore/image-private.h"
53#include "MagickCore/list.h"
54#include "MagickCore/magic.h"
55#include "MagickCore/magick.h"
56#include "MagickCore/memory_.h"
57#include "MagickCore/resource_.h"
58#include "MagickCore/pixel-accessor.h"
59#include "MagickCore/quantum-private.h"
60#include "MagickCore/shear.h"
61#include "MagickCore/static.h"
62#include "MagickCore/string_.h"
63#include "MagickCore/module.h"
64#include "MagickCore/transform.h"
65#include "MagickCore/utility.h"
66
67typedef struct
68   {
69   unsigned char Red;
70   unsigned char Blue;
71   unsigned char Green;
72   } RGB_Record;
73
74/* Default palette for WPG level 1 */
75const RGB_Record WPG1_Palette[256]={
76{  0,  0,  0},    {  0,  0,168},
77{  0,168,  0},    {  0,168,168},
78{168,  0,  0},    {168,  0,168},
79{168, 84,  0},    {168,168,168},
80{ 84, 84, 84},    { 84, 84,252},
81{ 84,252, 84},    { 84,252,252},
82{252, 84, 84},    {252, 84,252},
83{252,252, 84},    {252,252,252},  /*16*/
84{  0,  0,  0},    { 20, 20, 20},
85{ 32, 32, 32},    { 44, 44, 44},
86{ 56, 56, 56},    { 68, 68, 68},
87{ 80, 80, 80},    { 96, 96, 96},
88{112,112,112},    {128,128,128},
89{144,144,144},    {160,160,160},
90{180,180,180},    {200,200,200},
91{224,224,224},    {252,252,252},  /*32*/
92{  0,  0,252},    { 64,  0,252},
93{124,  0,252},    {188,  0,252},
94{252,  0,252},    {252,  0,188},
95{252,  0,124},    {252,  0, 64},
96{252,  0,  0},    {252, 64,  0},
97{252,124,  0},    {252,188,  0},
98{252,252,  0},    {188,252,  0},
99{124,252,  0},    { 64,252,  0},  /*48*/
100{  0,252,  0},    {  0,252, 64},
101{  0,252,124},    {  0,252,188},
102{  0,252,252},    {  0,188,252},
103{  0,124,252},    {  0, 64,252},
104{124,124,252},    {156,124,252},
105{188,124,252},    {220,124,252},
106{252,124,252},    {252,124,220},
107{252,124,188},    {252,124,156},  /*64*/
108{252,124,124},    {252,156,124},
109{252,188,124},    {252,220,124},
110{252,252,124},    {220,252,124},
111{188,252,124},    {156,252,124},
112{124,252,124},    {124,252,156},
113{124,252,188},    {124,252,220},
114{124,252,252},    {124,220,252},
115{124,188,252},    {124,156,252},  /*80*/
116{180,180,252},    {196,180,252},
117{216,180,252},    {232,180,252},
118{252,180,252},    {252,180,232},
119{252,180,216},    {252,180,196},
120{252,180,180},    {252,196,180},
121{252,216,180},    {252,232,180},
122{252,252,180},    {232,252,180},
123{216,252,180},    {196,252,180},  /*96*/
124{180,220,180},    {180,252,196},
125{180,252,216},    {180,252,232},
126{180,252,252},    {180,232,252},
127{180,216,252},    {180,196,252},
128{0,0,112},    {28,0,112},
129{56,0,112},    {84,0,112},
130{112,0,112},    {112,0,84},
131{112,0,56},    {112,0,28},  /*112*/
132{112,0,0},    {112,28,0},
133{112,56,0},    {112,84,0},
134{112,112,0},    {84,112,0},
135{56,112,0},    {28,112,0},
136{0,112,0},    {0,112,28},
137{0,112,56},    {0,112,84},
138{0,112,112},    {0,84,112},
139{0,56,112},    {0,28,112},   /*128*/
140{56,56,112},    {68,56,112},
141{84,56,112},    {96,56,112},
142{112,56,112},    {112,56,96},
143{112,56,84},    {112,56,68},
144{112,56,56},    {112,68,56},
145{112,84,56},    {112,96,56},
146{112,112,56},    {96,112,56},
147{84,112,56},    {68,112,56},  /*144*/
148{56,112,56},    {56,112,69},
149{56,112,84},    {56,112,96},
150{56,112,112},    {56,96,112},
151{56,84,112},    {56,68,112},
152{80,80,112},    {88,80,112},
153{96,80,112},    {104,80,112},
154{112,80,112},    {112,80,104},
155{112,80,96},    {112,80,88},  /*160*/
156{112,80,80},    {112,88,80},
157{112,96,80},    {112,104,80},
158{112,112,80},    {104,112,80},
159{96,112,80},    {88,112,80},
160{80,112,80},    {80,112,88},
161{80,112,96},    {80,112,104},
162{80,112,112},    {80,114,112},
163{80,96,112},    {80,88,112},  /*176*/
164{0,0,64},    {16,0,64},
165{32,0,64},    {48,0,64},
166{64,0,64},    {64,0,48},
167{64,0,32},    {64,0,16},
168{64,0,0},    {64,16,0},
169{64,32,0},    {64,48,0},
170{64,64,0},    {48,64,0},
171{32,64,0},    {16,64,0},  /*192*/
172{0,64,0},    {0,64,16},
173{0,64,32},    {0,64,48},
174{0,64,64},    {0,48,64},
175{0,32,64},    {0,16,64},
176{32,32,64},    {40,32,64},
177{48,32,64},    {56,32,64},
178{64,32,64},    {64,32,56},
179{64,32,48},    {64,32,40},  /*208*/
180{64,32,32},    {64,40,32},
181{64,48,32},    {64,56,32},
182{64,64,32},    {56,64,32},
183{48,64,32},    {40,64,32},
184{32,64,32},    {32,64,40},
185{32,64,48},    {32,64,56},
186{32,64,64},    {32,56,64},
187{32,48,64},    {32,40,64},  /*224*/
188{44,44,64},    {48,44,64},
189{52,44,64},    {60,44,64},
190{64,44,64},    {64,44,60},
191{64,44,52},    {64,44,48},
192{64,44,44},    {64,48,44},
193{64,52,44},    {64,60,44},
194{64,64,44},    {60,64,44},
195{52,64,44},    {48,64,44},  /*240*/
196{44,64,44},    {44,64,48},
197{44,64,52},    {44,64,60},
198{44,64,64},    {44,60,64},
199{44,55,64},    {44,48,64},
200{0,0,0},    {0,0,0},
201{0,0,0},    {0,0,0},
202{0,0,0},    {0,0,0},
203{0,0,0},    {0,0,0}    /*256*/
204};
205
206/*
207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208%                                                                             %
209%                                                                             %
210%                                                                             %
211%   I s W P G                                                                 %
212%                                                                             %
213%                                                                             %
214%                                                                             %
215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216%
217%  IsWPG() returns True if the image format type, identified by the magick
218%  string, is WPG.
219%
220%  The format of the IsWPG method is:
221%
222%      unsigned int IsWPG(const unsigned char *magick,const size_t length)
223%
224%  A description of each parameter follows:
225%
226%    o status:  Method IsWPG returns True if the image format type is WPG.
227%
228%    o magick: compare image format pattern against these bytes.
229%
230%    o length: Specifies the length of the magick string.
231%
232*/
233static unsigned int IsWPG(const unsigned char *magick,const size_t length)
234{
235  if (length < 4)
236    return(MagickFalse);
237  if (memcmp(magick,"\377WPC",4) == 0)
238    return(MagickTrue);
239  return(MagickFalse);
240}
241
242
243static void Rd_WP_DWORD(Image *image,size_t *d)
244{
245  unsigned char
246    b;
247
248  b=ReadBlobByte(image);
249  *d=b;
250  if (b < 0xFFU)
251    return;
252  b=ReadBlobByte(image);
253  *d=(size_t) b;
254  b=ReadBlobByte(image);
255  *d+=(size_t) b*256l;
256  if (*d < 0x8000)
257    return;
258  *d=(*d & 0x7FFF) << 16;
259  b=ReadBlobByte(image);
260  *d+=(size_t) b;
261  b=ReadBlobByte(image);
262  *d+=(size_t) b*256l;
263  return;
264}
265
266static void InsertRow(unsigned char *p,ssize_t y,Image *image, int bpp)
267{
268  ExceptionInfo
269    *exception;
270
271  int
272    bit;
273
274  Quantum
275    index;
276
277  register Quantum
278    *q;
279
280  ssize_t
281    x;
282
283  exception=(&image->exception);
284  switch (bpp)
285    {
286    case 1:  /* Convert bitmap scanline. */
287      {
288        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
289        if (q == (Quantum *) NULL)
290          break;
291        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
292        {
293          for (bit=0; bit < 8; bit++)
294          {
295            index=((*p) & (0x80 >> bit) ? 0x01 : 0x00);
296            SetPixelIndex(image,index,q);
297            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
298            q+=GetPixelChannels(image);
299          }
300          p++;
301        }
302        if ((image->columns % 8) != 0)
303          {
304            for (bit=0; bit < (ssize_t) (image->columns % 8); bit++)
305            {
306              index=((*p) & (0x80 >> bit) ? 0x01 : 0x00);
307              SetPixelIndex(image,index,q);
308              SetPixelPacket(image,image->colormap+(ssize_t) index,q);
309              q+=GetPixelChannels(image);
310            }
311            p++;
312          }
313        if (!SyncAuthenticPixels(image,exception))
314          break;
315        break;
316      }
317    case 2:  /* Convert PseudoColor scanline. */
318      {
319        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
320        if (q == (Quantum *) NULL)
321          break;
322        for (x=0; x < ((ssize_t) image->columns-1); x+=2)
323        {
324            index=ConstrainColormapIndex(image,(*p >> 6) & 0x3);
325            SetPixelIndex(image,index,q);
326            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
327            q+=GetPixelChannels(image);
328            index=ConstrainColormapIndex(image,(*p >> 4) & 0x3);
329            SetPixelIndex(image,index,q);
330            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
331            q+=GetPixelChannels(image);
332            index=ConstrainColormapIndex(image,(*p >> 2) & 0x3);
333            SetPixelIndex(image,index,q);
334            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
335            q+=GetPixelChannels(image);
336            index=ConstrainColormapIndex(image,(*p) & 0x3);
337            SetPixelIndex(image,index,q);
338            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
339            p++;
340            q+=GetPixelChannels(image);
341        }
342       if ((image->columns % 4) != 0)
343          {
344            index=ConstrainColormapIndex(image,(*p >> 6) & 0x3);
345            SetPixelIndex(image,index,q);
346            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
347            q+=GetPixelChannels(image);
348            if ((image->columns % 4) >= 1)
349
350              {
351                index=ConstrainColormapIndex(image,(*p >> 4) & 0x3);
352                SetPixelIndex(image,index,q);
353                SetPixelPacket(image,image->colormap+(ssize_t) index,q);
354                q+=GetPixelChannels(image);
355                if ((image->columns % 4) >= 2)
356
357                  {
358                    index=ConstrainColormapIndex(image,(*p >> 2) & 0x3);
359                    SetPixelIndex(image,index,q);
360                    SetPixelPacket(image,image->colormap+(ssize_t) index,q);
361                    q+=GetPixelChannels(image);
362                  }
363              }
364            p++;
365          }
366        if (SyncAuthenticPixels(image,exception) == MagickFalse)
367          break;
368        break;
369      }
370
371    case 4:  /* Convert PseudoColor scanline. */
372      {
373        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
374        if (q == (Quantum *) NULL)
375          break;
376        for (x=0; x < ((ssize_t) image->columns-1); x+=2)
377          {
378            index=ConstrainColormapIndex(image,(*p >> 4) & 0x0f);
379            SetPixelIndex(image,index,q);
380            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
381            q+=GetPixelChannels(image);
382            index=ConstrainColormapIndex(image,(*p) & 0x0f);
383            SetPixelIndex(image,index,q);
384            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
385            p++;
386            q+=GetPixelChannels(image);
387          }
388        if ((image->columns % 2) != 0)
389          {
390            index=ConstrainColormapIndex(image,(*p >> 4) & 0x0f);
391            SetPixelIndex(image,index,q);
392            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
393            p++;
394            q+=GetPixelChannels(image);
395          }
396        if (SyncAuthenticPixels(image,exception) == MagickFalse)
397          break;
398        break;
399      }
400    case 8: /* Convert PseudoColor scanline. */
401      {
402        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
403        if (q == (Quantum *) NULL) break;
404
405        for (x=0; x < (ssize_t) image->columns; x++)
406          {
407            index=ConstrainColormapIndex(image,*p);
408            SetPixelIndex(image,index,q);
409            SetPixelPacket(image,image->colormap+(ssize_t) index,q);
410            p++;
411            q+=GetPixelChannels(image);
412          }
413        if (SyncAuthenticPixels(image,exception) == MagickFalse)
414          break;
415      }
416      break;
417
418    case 24:     /*  Convert DirectColor scanline.  */
419      q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
420      if (q == (Quantum *) NULL)
421        break;
422      for (x=0; x < (ssize_t) image->columns; x++)
423        {
424          SetPixelRed(image,ScaleCharToQuantum(*p++),q);
425          SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
426          SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
427          q+=GetPixelChannels(image);
428        }
429      if (!SyncAuthenticPixels(image,exception))
430        break;
431      break;
432    }
433}
434
435
436/* Helper for WPG1 raster reader. */
437#define InsertByte(b) \
438{ \
439  BImgBuff[x]=b; \
440  x++; \
441  if((ssize_t) x>=ldblk) \
442  { \
443    InsertRow(BImgBuff,(ssize_t) y,image,bpp); \
444    x=0; \
445    y++; \
446    } \
447}
448/* WPG1 raster reader. */
449static int UnpackWPGRaster(Image *image,int bpp)
450{
451  int
452    x,
453    y,
454    i;
455
456  unsigned char
457    bbuf,
458    *BImgBuff,
459    RunCount;
460
461  ssize_t
462    ldblk;
463
464  x=0;
465  y=0;
466
467  ldblk=(ssize_t) ((bpp*image->columns+7)/8);
468  BImgBuff=(unsigned char *) AcquireQuantumMemory((size_t) ldblk,
469    sizeof(*BImgBuff));
470  if(BImgBuff==NULL) return(-2);
471
472  while(y<(ssize_t) image->rows)
473    {
474      bbuf=ReadBlobByte(image);
475
476      RunCount=bbuf & 0x7F;
477      if(bbuf & 0x80)
478        {
479          if(RunCount)  /* repeat next byte runcount * */
480            {
481              bbuf=ReadBlobByte(image);
482              for(i=0;i<(int) RunCount;i++) InsertByte(bbuf);
483            }
484          else {  /* read next byte as RunCount; repeat 0xFF runcount* */
485            RunCount=ReadBlobByte(image);
486            for(i=0;i<(int) RunCount;i++) InsertByte(0xFF);
487          }
488        }
489      else {
490        if(RunCount)   /* next runcount byte are readed directly */
491          {
492            for(i=0;i < (int) RunCount;i++)
493              {
494                bbuf=ReadBlobByte(image);
495                InsertByte(bbuf);
496              }
497          }
498        else {  /* repeat previous line runcount* */
499          RunCount=ReadBlobByte(image);
500          if(x) {    /* attempt to duplicate row from x position: */
501            /* I do not know what to do here */
502            BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
503            return(-3);
504          }
505          for(i=0;i < (int) RunCount;i++)
506            {
507              x=0;
508              y++;    /* Here I need to duplicate previous row RUNCOUNT* */
509              if(y<2) continue;
510              if(y>(ssize_t) image->rows)
511                {
512                  BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
513                  return(-4);
514                }
515              InsertRow(BImgBuff,y-1,image,bpp);
516            }
517        }
518      }
519    }
520  BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
521  return(0);
522}
523
524
525/* Helper for WPG2 reader. */
526#define InsertByte6(b) \
527{ \
528  if(XorMe)\
529    BImgBuff[x] = (unsigned char)~b;\
530  else\
531    BImgBuff[x] = b;\
532  x++; \
533  if((ssize_t) x >= ldblk) \
534  { \
535    InsertRow(BImgBuff,(ssize_t) y,image,bpp); \
536    x=0; \
537    y++; \
538   } \
539}
540/* WPG2 raster reader. */
541static int UnpackWPG2Raster(Image *image,int bpp)
542{
543  size_t
544    x,
545    y;
546
547  ssize_t
548    ldblk;
549
550  int XorMe = 0;
551
552  unsigned int
553    SampleSize=1;
554
555  unsigned char
556    bbuf,
557    *BImgBuff,
558    RunCount,
559    SampleBuffer[8];
560
561  unsigned int
562    i;
563
564  x=0;
565  y=0;
566  ldblk=(ssize_t) ((bpp*image->columns+7)/8);
567  BImgBuff=(unsigned char *) AcquireQuantumMemory((size_t) ldblk,
568    sizeof(*BImgBuff));
569  if(BImgBuff==NULL)
570    return(-2);
571
572  while( y< image->rows)
573    {
574      bbuf=ReadBlobByte(image);
575
576      switch(bbuf)
577        {
578        case 0x7D:
579          SampleSize=ReadBlobByte(image);  /* DSZ */
580          if(SampleSize>8)
581            return(-2);
582          if(SampleSize<1)
583            return(-2);
584          break;
585        case 0x7E:
586          (void) FormatLocaleFile(stderr,
587            "\nUnsupported WPG token XOR, please report!");
588          XorMe=!XorMe;
589          break;
590        case 0x7F:
591          RunCount=ReadBlobByte(image);   /* BLK */
592          for(i=0; i < SampleSize*(RunCount+1); i++)
593            {
594              InsertByte6(0);
595            }
596          break;
597        case 0xFD:
598          RunCount=ReadBlobByte(image);   /* EXT */
599          for(i=0; i<= RunCount;i++)
600            for(bbuf=0; bbuf < SampleSize; bbuf++)
601              InsertByte6(SampleBuffer[bbuf]);
602          break;
603        case 0xFE:
604          RunCount=ReadBlobByte(image);  /* RST */
605          if(x!=0)
606            {
607              (void) FormatLocaleFile(stderr,
608                "\nUnsupported WPG2 unaligned token RST x=%.20g, please report!\n"
609                ,(double) x);
610              return(-3);
611            }
612          {
613            /* duplicate the previous row RunCount x */
614            for(i=0;i<=RunCount;i++)
615              {
616                InsertRow(BImgBuff,(ssize_t) (image->rows >= y ? y : image->rows-1),
617                          image,bpp);
618                y++;
619              }
620          }
621          break;
622        case 0xFF:
623          RunCount=ReadBlobByte(image);   /* WHT */
624          for(i=0; i < SampleSize*(RunCount+1); i++)
625            {
626              InsertByte6(0xFF);
627            }
628          break;
629        default:
630          RunCount=bbuf & 0x7F;
631
632          if(bbuf & 0x80)     /* REP */
633            {
634              for(i=0; i < SampleSize; i++)
635                SampleBuffer[i]=ReadBlobByte(image);
636              for(i=0;i<=RunCount;i++)
637                for(bbuf=0;bbuf<SampleSize;bbuf++)
638                  InsertByte6(SampleBuffer[bbuf]);
639            }
640          else {      /* NRP */
641            for(i=0; i< SampleSize*(RunCount+1);i++)
642              {
643                bbuf=ReadBlobByte(image);
644                InsertByte6(bbuf);
645              }
646          }
647        }
648    }
649  BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);
650  return(0);
651}
652
653
654typedef float tCTM[3][3];
655
656static unsigned LoadWPG2Flags(Image *image,char Precision,float *Angle,tCTM *CTM)
657{
658const unsigned char TPR=1,TRN=2,SKW=4,SCL=8,ROT=0x10,OID=0x20,LCK=0x80;
659ssize_t x;
660unsigned DenX;
661unsigned Flags;
662
663 (void) memset(*CTM,0,sizeof(*CTM));     /*CTM.erase();CTM.resize(3,3);*/
664 (*CTM)[0][0]=1;
665 (*CTM)[1][1]=1;
666 (*CTM)[2][2]=1;
667
668 Flags=ReadBlobLSBShort(image);
669 if(Flags & LCK) x=ReadBlobLSBLong(image);  /*Edit lock*/
670 if(Flags & OID)
671  {
672  if(Precision==0)
673    {x=ReadBlobLSBShort(image);}  /*ObjectID*/
674  else
675    {x=ReadBlobLSBLong(image);}  /*ObjectID (Double precision)*/
676  }
677 if(Flags & ROT)
678  {
679  x=ReadBlobLSBLong(image);  /*Rot Angle*/
680  if(Angle) *Angle=x/65536.0;
681  }
682 if(Flags & (ROT|SCL))
683  {
684  x=ReadBlobLSBLong(image);  /*Sx*cos()*/
685  (*CTM)[0][0] = (float)x/0x10000;
686  x=ReadBlobLSBLong(image);  /*Sy*cos()*/
687  (*CTM)[1][1] = (float)x/0x10000;
688  }
689 if(Flags & (ROT|SKW))
690  {
691  x=ReadBlobLSBLong(image);       /*Kx*sin()*/
692  (*CTM)[1][0] = (float)x/0x10000;
693  x=ReadBlobLSBLong(image);       /*Ky*sin()*/
694  (*CTM)[0][1] = (float)x/0x10000;
695  }
696 if(Flags & TRN)
697  {
698  x=ReadBlobLSBLong(image); DenX=ReadBlobLSBShort(image);  /*Tx*/
699        if(x>=0) (*CTM)[0][2] = (float)x+(float)DenX/0x10000;
700            else (*CTM)[0][2] = (float)x-(float)DenX/0x10000;
701  x=ReadBlobLSBLong(image); DenX=ReadBlobLSBShort(image);  /*Ty*/
702  (*CTM)[1][2]=(float)x + ((x>=0)?1:-1)*(float)DenX/0x10000;
703        if(x>=0) (*CTM)[1][2] = (float)x+(float)DenX/0x10000;
704            else (*CTM)[1][2] = (float)x-(float)DenX/0x10000;
705  }
706 if(Flags & TPR)
707  {
708  x=ReadBlobLSBShort(image); DenX=ReadBlobLSBShort(image);  /*Px*/
709  (*CTM)[2][0] = x + (float)DenX/0x10000;;
710  x=ReadBlobLSBShort(image);  DenX=ReadBlobLSBShort(image); /*Py*/
711  (*CTM)[2][1] = x + (float)DenX/0x10000;
712  }
713 return(Flags);
714}
715
716
717static Image *ExtractPostscript(Image *image,const ImageInfo *image_info,
718  MagickOffsetType PS_Offset,ssize_t PS_Size,ExceptionInfo *exception)
719{
720  char
721    postscript_file[MaxTextExtent];
722
723  const MagicInfo
724    *magic_info;
725
726  FILE
727    *ps_file;
728
729  ImageInfo
730    *clone_info;
731
732  Image
733    *image2;
734
735  unsigned char
736    magick[2*MaxTextExtent];
737
738
739  if ((clone_info=CloneImageInfo(image_info)) == NULL)
740    return(image);
741  clone_info->blob=(void *) NULL;
742  clone_info->length=0;
743
744  /* Obtain temporary file */
745  AcquireUniqueFilename(postscript_file);
746  ps_file=OpenMagickStream(postscript_file,"wb");
747  if (ps_file == (FILE *) NULL)
748    goto FINISH;
749
750  /* Copy postscript to temporary file */
751  (void) SeekBlob(image,PS_Offset,SEEK_SET);
752  (void) ReadBlob(image, 2*MaxTextExtent, magick);
753
754  (void) SeekBlob(image,PS_Offset,SEEK_SET);
755  while(PS_Size-- > 0)
756    {
757      (void) fputc(ReadBlobByte(image),ps_file);
758    }
759  (void) fclose(ps_file);
760
761    /* Detect file format - Check magic.mgk configuration file. */
762  magic_info=GetMagicInfo(magick,2*MaxTextExtent,exception);
763  if(magic_info == (const MagicInfo *) NULL) goto FINISH_UNL;
764  /*     printf("Detected:%s  \n",magic_info->name); */
765  if(exception->severity != UndefinedException) goto FINISH_UNL;
766  if(magic_info->name == (char *) NULL) goto FINISH_UNL;
767
768  (void) CopyMagickMemory(clone_info->magick,magic_info->name,MaxTextExtent);
769
770    /* Read nested image */
771  /*FormatString(clone_info->filename,"%s:%s",magic_info->name,postscript_file);*/
772  FormatLocaleString(clone_info->filename,MaxTextExtent,"%s",postscript_file);
773  image2=ReadImage(clone_info,exception);
774
775  if (!image2)
776    goto FINISH_UNL;
777
778  /*
779    Replace current image with new image while copying base image
780    attributes.
781  */
782  (void) CopyMagickMemory(image2->filename,image->filename,MaxTextExtent);
783  (void) CopyMagickMemory(image2->magick_filename,image->magick_filename,MaxTextExtent);
784  (void) CopyMagickMemory(image2->magick,image->magick,MaxTextExtent);
785  image2->depth=image->depth;
786  DestroyBlob(image2);
787  image2->blob=ReferenceBlob(image->blob);
788
789  if ((image->rows == 0) || (image->columns == 0))
790    DeleteImageFromList(&image);
791
792  AppendImageToList(&image,image2);
793
794 FINISH_UNL:
795  (void) RelinquishUniqueFileResource(postscript_file);
796 FINISH:
797  DestroyImageInfo(clone_info);
798  return(image);
799}
800
801/*
802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803%                                                                             %
804%                                                                             %
805%                                                                             %
806%   R e a d W P G I m a g e                                                   %
807%                                                                             %
808%                                                                             %
809%                                                                             %
810%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
811%
812%  Method ReadWPGImage reads an WPG X image file and returns it.  It
813%  allocates the memory necessary for the new Image structure and returns a
814%  pointer to the new image.
815%
816%  The format of the ReadWPGImage method is:
817%
818%    Image *ReadWPGImage(const ImageInfo *image_info,ExceptionInfo *exception)
819%
820%  A description of each parameter follows:
821%
822%    o image:  Method ReadWPGImage returns a pointer to the image after
823%      reading. A null image is returned if there is a memory shortage or if
824%      the image cannot be read.
825%
826%    o image_info: Specifies a pointer to a ImageInfo structure.
827%
828%    o exception: return any errors or warnings in this structure.
829%
830*/
831static Image *ReadWPGImage(const ImageInfo *image_info,
832  ExceptionInfo *exception)
833{
834  typedef struct
835  {
836    size_t FileId;
837    MagickOffsetType DataOffset;
838    unsigned int ProductType;
839    unsigned int FileType;
840    unsigned char MajorVersion;
841    unsigned char MinorVersion;
842    unsigned int EncryptKey;
843    unsigned int Reserved;
844  } WPGHeader;
845
846  typedef struct
847  {
848    unsigned char RecType;
849    size_t RecordLength;
850  } WPGRecord;
851
852  typedef struct
853  {
854    unsigned char Class;
855    unsigned char RecType;
856    size_t Extension;
857    size_t RecordLength;
858  } WPG2Record;
859
860  typedef struct
861  {
862    unsigned  HorizontalUnits;
863    unsigned  VerticalUnits;
864    unsigned char PosSizePrecision;
865  } WPG2Start;
866
867  typedef struct
868  {
869    unsigned int Width;
870    unsigned int Heigth;
871    unsigned int Depth;
872    unsigned int HorzRes;
873    unsigned int VertRes;
874  } WPGBitmapType1;
875
876  typedef struct
877  {
878    unsigned int Width;
879    unsigned int Heigth;
880    unsigned char Depth;
881    unsigned char Compression;
882  } WPG2BitmapType1;
883
884  typedef struct
885  {
886    unsigned int RotAngle;
887    unsigned int LowLeftX;
888    unsigned int LowLeftY;
889    unsigned int UpRightX;
890    unsigned int UpRightY;
891    unsigned int Width;
892    unsigned int Heigth;
893    unsigned int Depth;
894    unsigned int HorzRes;
895    unsigned int VertRes;
896  } WPGBitmapType2;
897
898  typedef struct
899  {
900    unsigned int StartIndex;
901    unsigned int NumOfEntries;
902  } WPGColorMapRec;
903
904  typedef struct {
905    size_t PS_unknown1;
906    unsigned int PS_unknown2;
907    unsigned int PS_unknown3;
908  } WPGPSl1Record;
909
910  Image
911    *image,
912    *rotated_image;
913
914  unsigned int
915    status;
916
917  WPGHeader
918    Header;
919
920  WPGRecord
921    Rec;
922
923  WPG2Record
924    Rec2;
925
926  WPG2Start StartWPG;
927
928  WPGBitmapType1
929    BitmapHeader1;
930
931  WPG2BitmapType1
932    Bitmap2Header1;
933
934  WPGBitmapType2
935    BitmapHeader2;
936
937  WPGColorMapRec
938    WPG_Palette;
939
940  int
941    i,
942    bpp,
943    WPG2Flags;
944
945  ssize_t
946    ldblk;
947
948  size_t
949    one;
950
951  unsigned char
952    *BImgBuff;
953
954  tCTM CTM;         /*current transform matrix*/
955
956  /*
957    Open image file.
958  */
959  assert(image_info != (const ImageInfo *) NULL);
960  assert(image_info->signature == MagickSignature);
961  assert(exception != (ExceptionInfo *) NULL);
962  assert(exception->signature == MagickSignature);
963  one=1;
964  image=AcquireImage(image_info);
965  image->depth=8;
966  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
967  if (status == MagickFalse)
968    {
969      image=DestroyImageList(image);
970      return((Image *) NULL);
971    }
972  /*
973    Read WPG image.
974  */
975  Header.FileId=ReadBlobLSBLong(image);
976  Header.DataOffset=(MagickOffsetType) ReadBlobLSBLong(image);
977  Header.ProductType=ReadBlobLSBShort(image);
978  Header.FileType=ReadBlobLSBShort(image);
979  Header.MajorVersion=ReadBlobByte(image);
980  Header.MinorVersion=ReadBlobByte(image);
981  Header.EncryptKey=ReadBlobLSBShort(image);
982  Header.Reserved=ReadBlobLSBShort(image);
983
984  if (Header.FileId!=0x435057FF || (Header.ProductType>>8)!=0x16)
985    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
986  if (Header.EncryptKey!=0)
987    ThrowReaderException(CoderError,"EncryptedWPGImageFileNotSupported");
988
989  image->columns = 1;
990  image->rows = 1;
991  image->colors = 0;
992  bpp=0;
993  BitmapHeader2.RotAngle=0;
994
995  switch(Header.FileType)
996    {
997    case 1:     /* WPG level 1 */
998      while(!EOFBlob(image)) /* object parser loop */
999        {
1000          (void) SeekBlob(image,Header.DataOffset,SEEK_SET);
1001          if(EOFBlob(image))
1002            break;
1003
1004          Rec.RecType=(i=ReadBlobByte(image));
1005          if(i==EOF)
1006            break;
1007          Rd_WP_DWORD(image,&Rec.RecordLength);
1008          if(EOFBlob(image))
1009            break;
1010
1011          Header.DataOffset=TellBlob(image)+Rec.RecordLength;
1012
1013          switch(Rec.RecType)
1014            {
1015            case 0x0B: /* bitmap type 1 */
1016              BitmapHeader1.Width=ReadBlobLSBShort(image);
1017              BitmapHeader1.Heigth=ReadBlobLSBShort(image);
1018              BitmapHeader1.Depth=ReadBlobLSBShort(image);
1019              BitmapHeader1.HorzRes=ReadBlobLSBShort(image);
1020              BitmapHeader1.VertRes=ReadBlobLSBShort(image);
1021
1022              if(BitmapHeader1.HorzRes && BitmapHeader1.VertRes)
1023                {
1024                  image->units=PixelsPerCentimeterResolution;
1025                  image->x_resolution=BitmapHeader1.HorzRes/470.0;
1026                  image->y_resolution=BitmapHeader1.VertRes/470.0;
1027                }
1028              image->columns=BitmapHeader1.Width;
1029              image->rows=BitmapHeader1.Heigth;
1030              bpp=BitmapHeader1.Depth;
1031
1032              goto UnpackRaster;
1033
1034            case 0x0E:  /*Color palette */
1035              WPG_Palette.StartIndex=ReadBlobLSBShort(image);
1036              WPG_Palette.NumOfEntries=ReadBlobLSBShort(image);
1037
1038              image->colors=WPG_Palette.NumOfEntries;
1039              if (!AcquireImageColormap(image,image->colors,exception))
1040                goto NoMemory;
1041              for (i=WPG_Palette.StartIndex;
1042                   i < (int)WPG_Palette.NumOfEntries; i++)
1043                {
1044                  image->colormap[i].red=ScaleCharToQuantum((unsigned char)
1045                    ReadBlobByte(image));
1046                  image->colormap[i].green=ScaleCharToQuantum((unsigned char)
1047                    ReadBlobByte(image));
1048                  image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
1049                    ReadBlobByte(image));
1050                }
1051              break;
1052
1053            case 0x11:  /* Start PS l1 */
1054              if(Rec.RecordLength > 8)
1055                image=ExtractPostscript(image,image_info,
1056                  TellBlob(image)+8,   /* skip PS header in the wpg */
1057                  (ssize_t) Rec.RecordLength-8,exception);
1058              break;
1059
1060            case 0x14:  /* bitmap type 2 */
1061              BitmapHeader2.RotAngle=ReadBlobLSBShort(image);
1062              BitmapHeader2.LowLeftX=ReadBlobLSBShort(image);
1063              BitmapHeader2.LowLeftY=ReadBlobLSBShort(image);
1064              BitmapHeader2.UpRightX=ReadBlobLSBShort(image);
1065              BitmapHeader2.UpRightY=ReadBlobLSBShort(image);
1066              BitmapHeader2.Width=ReadBlobLSBShort(image);
1067              BitmapHeader2.Heigth=ReadBlobLSBShort(image);
1068              BitmapHeader2.Depth=ReadBlobLSBShort(image);
1069              BitmapHeader2.HorzRes=ReadBlobLSBShort(image);
1070              BitmapHeader2.VertRes=ReadBlobLSBShort(image);
1071
1072              image->units=PixelsPerCentimeterResolution;
1073              image->page.width=(unsigned int)
1074                ((BitmapHeader2.LowLeftX-BitmapHeader2.UpRightX)/470.0);
1075              image->page.height=(unsigned int)
1076                ((BitmapHeader2.LowLeftX-BitmapHeader2.UpRightY)/470.0);
1077              image->page.x=(int) (BitmapHeader2.LowLeftX/470.0);
1078              image->page.y=(int) (BitmapHeader2.LowLeftX/470.0);
1079              if(BitmapHeader2.HorzRes && BitmapHeader2.VertRes)
1080                {
1081                  image->x_resolution=BitmapHeader2.HorzRes/470.0;
1082                  image->y_resolution=BitmapHeader2.VertRes/470.0;
1083                }
1084              image->columns=BitmapHeader2.Width;
1085              image->rows=BitmapHeader2.Heigth;
1086              bpp=BitmapHeader2.Depth;
1087
1088            UnpackRaster:
1089              if ((image->colors == 0) && (bpp != 24))
1090                {
1091                  image->colors=one << bpp;
1092                  if (!AcquireImageColormap(image,image->colors,exception))
1093                    {
1094                    NoMemory:
1095                      ThrowReaderException(ResourceLimitError,
1096                        "MemoryAllocationFailed");
1097                    }
1098                  /* printf("Load default colormap \n"); */
1099                  for (i=0; (i < (int) image->colors) && (i < 256); i++)
1100                    {
1101                      image->colormap[i].red=ScaleCharToQuantum(WPG1_Palette[i].Red);
1102                      image->colormap[i].green=ScaleCharToQuantum(WPG1_Palette[i].Green);
1103                      image->colormap[i].blue=ScaleCharToQuantum(WPG1_Palette[i].Blue);
1104                    }
1105                }
1106              else
1107                {
1108                  if (bpp < 24)
1109                    if ( (image->colors < (one << bpp)) && (bpp != 24) )
1110                      image->colormap=(PixelPacket *) ResizeQuantumMemory(
1111                        image->colormap,(size_t) (one << bpp),
1112                        sizeof(*image->colormap));
1113                }
1114
1115              if (bpp == 1)
1116                {
1117                  if(image->colormap[0].red==0 &&
1118                     image->colormap[0].green==0 &&
1119                     image->colormap[0].blue==0 &&
1120                     image->colormap[1].red==0 &&
1121                     image->colormap[1].green==0 &&
1122                     image->colormap[1].blue==0)
1123                    {  /* fix crippled monochrome palette */
1124                      image->colormap[1].red =
1125                        image->colormap[1].green =
1126                        image->colormap[1].blue = QuantumRange;
1127                    }
1128                }
1129
1130              if(UnpackWPGRaster(image,bpp) < 0)
1131                /* The raster cannot be unpacked */
1132                {
1133                DecompressionFailed:
1134                  ThrowReaderException(CoderError,"UnableToDecompressImage");
1135                    }
1136
1137              if(Rec.RecType==0x14 && BitmapHeader2.RotAngle!=0 && !image_info->ping)
1138                {
1139                  /* flop command */
1140                  if(BitmapHeader2.RotAngle & 0x8000)
1141                    {
1142                      rotated_image = FlopImage(image, exception);
1143                      rotated_image->blob = image->blob;
1144                      DuplicateBlob(rotated_image,image);
1145                      (void) RemoveLastImageFromList(&image);
1146                      AppendImageToList(&image,rotated_image);
1147                    }
1148                  /* flip command */
1149                  if(BitmapHeader2.RotAngle & 0x2000)
1150                    {
1151                      rotated_image = FlipImage(image, exception);
1152                      rotated_image->blob = image->blob;
1153                      DuplicateBlob(rotated_image,image);
1154                      (void) RemoveLastImageFromList(&image);
1155                      AppendImageToList(&image,rotated_image);
1156                    }
1157
1158      /* rotate command */
1159                  if(BitmapHeader2.RotAngle & 0x0FFF)
1160                    {
1161                      rotated_image = RotateImage(image, (BitmapHeader2.RotAngle & 0x0FFF), exception);
1162                      rotated_image->blob = image->blob;
1163                      DuplicateBlob(rotated_image,image);
1164                      (void) RemoveLastImageFromList(&image);
1165                      AppendImageToList(&image,rotated_image);
1166                    }
1167                }
1168
1169              /* Allocate next image structure. */
1170              AcquireNextImage(image_info,image);
1171              image->depth=8;
1172              if (image->next == (Image *) NULL)
1173                goto Finish;
1174              image=SyncNextImageInList(image);
1175              image->columns=image->rows=0;
1176              image->colors=0;
1177              break;
1178
1179            case 0x1B:  /* Postscript l2 */
1180              if(Rec.RecordLength>0x3C)
1181                image=ExtractPostscript(image,image_info,
1182                  TellBlob(image)+0x3C,   /* skip PS l2 header in the wpg */
1183                  (ssize_t) Rec.RecordLength-0x3C,exception);
1184              break;
1185            }
1186        }
1187      break;
1188
1189    case 2:  /* WPG level 2 */
1190      (void) memset(CTM,0,sizeof(CTM));
1191      StartWPG.PosSizePrecision = 0;
1192      while(!EOFBlob(image)) /* object parser loop */
1193        {
1194          (void) SeekBlob(image,Header.DataOffset,SEEK_SET);
1195          if(EOFBlob(image))
1196            break;
1197
1198          Rec2.Class=(i=ReadBlobByte(image));
1199          if(i==EOF)
1200            break;
1201          Rec2.RecType=(i=ReadBlobByte(image));
1202          if(i==EOF)
1203            break;
1204          Rd_WP_DWORD(image,&Rec2.Extension);
1205          Rd_WP_DWORD(image,&Rec2.RecordLength);
1206          if(EOFBlob(image))
1207            break;
1208
1209          Header.DataOffset=TellBlob(image)+Rec2.RecordLength;
1210
1211          switch(Rec2.RecType)
1212            {
1213      case 1:
1214              StartWPG.HorizontalUnits=ReadBlobLSBShort(image);
1215              StartWPG.VerticalUnits=ReadBlobLSBShort(image);
1216              StartWPG.PosSizePrecision=ReadBlobByte(image);
1217              break;
1218            case 0x0C:    /* Color palette */
1219              WPG_Palette.StartIndex=ReadBlobLSBShort(image);
1220              WPG_Palette.NumOfEntries=ReadBlobLSBShort(image);
1221
1222              image->colors=WPG_Palette.NumOfEntries;
1223              if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
1224                ThrowReaderException(ResourceLimitError,
1225                  "MemoryAllocationFailed");
1226              for (i=WPG_Palette.StartIndex;
1227                   i < (int)WPG_Palette.NumOfEntries; i++)
1228                {
1229                  image->colormap[i].red=ScaleCharToQuantum((char)
1230                    ReadBlobByte(image));
1231                  image->colormap[i].green=ScaleCharToQuantum((char)
1232                    ReadBlobByte(image));
1233                  image->colormap[i].blue=ScaleCharToQuantum((char)
1234                    ReadBlobByte(image));
1235                  (void) ReadBlobByte(image);   /*Opacity??*/
1236                }
1237              break;
1238            case 0x0E:
1239              Bitmap2Header1.Width=ReadBlobLSBShort(image);
1240              Bitmap2Header1.Heigth=ReadBlobLSBShort(image);
1241              Bitmap2Header1.Depth=ReadBlobByte(image);
1242              Bitmap2Header1.Compression=ReadBlobByte(image);
1243
1244              if(Bitmap2Header1.Compression > 1)
1245                continue; /*Unknown compression method */
1246              switch(Bitmap2Header1.Depth)
1247                {
1248                case 1:
1249                  bpp=1;
1250                  break;
1251                case 2:
1252                  bpp=2;
1253                  break;
1254                case 3:
1255                  bpp=4;
1256                  break;
1257                case 4:
1258                  bpp=8;
1259                  break;
1260                case 8:
1261                  bpp=24;
1262                  break;
1263                default:
1264                  continue;  /*Ignore raster with unknown depth*/
1265                }
1266              image->columns=Bitmap2Header1.Width;
1267              image->rows=Bitmap2Header1.Heigth;
1268
1269              if ((image->colors == 0) && (bpp != 24))
1270                {
1271                  size_t
1272                    one;
1273
1274                  one=1;
1275                  image->colors=one << bpp;
1276                  if (!AcquireImageColormap(image,image->colors,exception))
1277                    goto NoMemory;
1278                }
1279              else
1280                {
1281                  if(bpp < 24)
1282                    if( image->colors<(one << bpp) && bpp!=24 )
1283                      image->colormap=(PixelPacket *) ResizeQuantumMemory(
1284                       image->colormap,(size_t) (one << bpp),
1285                       sizeof(*image->colormap));
1286                }
1287
1288
1289              switch(Bitmap2Header1.Compression)
1290                {
1291                case 0:    /*Uncompressed raster*/
1292                  {
1293                    ldblk=(ssize_t) ((bpp*image->columns+7)/8);
1294                    BImgBuff=(unsigned char *) AcquireQuantumMemory((size_t)
1295                      ldblk,sizeof(*BImgBuff));
1296                    if (BImgBuff == (unsigned char *) NULL)
1297                      goto NoMemory;
1298
1299                    for(i=0; i< (ssize_t) image->rows; i++)
1300                      {
1301                        (void) ReadBlob(image,ldblk,BImgBuff);
1302                        InsertRow(BImgBuff,i,image,bpp);
1303                      }
1304
1305                    if(BImgBuff)
1306                      BImgBuff=(unsigned char *) RelinquishMagickMemory(BImgBuff);;
1307                    break;
1308                  }
1309                case 1:    /*RLE for WPG2 */
1310                  {
1311                    if( UnpackWPG2Raster(image,bpp) < 0)
1312                      goto DecompressionFailed;
1313                    break;
1314                  }
1315                }
1316
1317              if(CTM[0][0]<0 && !image_info->ping)
1318    {    /*?? RotAngle=360-RotAngle;*/
1319      rotated_image = FlopImage(image, exception);
1320      rotated_image->blob = image->blob;
1321      DuplicateBlob(rotated_image,image);
1322      (void) RemoveLastImageFromList(&image);
1323      AppendImageToList(&image,rotated_image);
1324                  /* Try to change CTM according to Flip - I am not sure, must be checked.
1325                     Tx(0,0)=-1;      Tx(1,0)=0;   Tx(2,0)=0;
1326                     Tx(0,1)= 0;      Tx(1,1)=1;   Tx(2,1)=0;
1327                     Tx(0,2)=(WPG._2Rect.X_ur+WPG._2Rect.X_ll);
1328                     Tx(1,2)=0;   Tx(2,2)=1; */
1329                }
1330              if(CTM[1][1]<0 && !image_info->ping)
1331    {    /*?? RotAngle=360-RotAngle;*/
1332      rotated_image = FlipImage(image, exception);
1333      rotated_image->blob = image->blob;
1334      DuplicateBlob(rotated_image,image);
1335      (void) RemoveLastImageFromList(&image);
1336      AppendImageToList(&image,rotated_image);
1337                  /* Try to change CTM according to Flip - I am not sure, must be checked.
1338                     float_matrix Tx(3,3);
1339                     Tx(0,0)= 1;   Tx(1,0)= 0;   Tx(2,0)=0;
1340                     Tx(0,1)= 0;   Tx(1,1)=-1;   Tx(2,1)=0;
1341                     Tx(0,2)= 0;   Tx(1,2)=(WPG._2Rect.Y_ur+WPG._2Rect.Y_ll);
1342                     Tx(2,2)=1; */
1343    }
1344
1345
1346              /* Allocate next image structure. */
1347              AcquireNextImage(image_info,image);
1348              image->depth=8;
1349              if (image->next == (Image *) NULL)
1350                goto Finish;
1351              image=SyncNextImageInList(image);
1352              image->columns=image->rows=1;
1353              image->colors=0;
1354              break;
1355
1356            case 0x12:  /* Postscript WPG2*/
1357        i=ReadBlobLSBShort(image);
1358              if(Rec2.RecordLength > (unsigned int) i)
1359                image=ExtractPostscript(image,image_info,
1360                  TellBlob(image)+i,    /*skip PS header in the wpg2*/
1361                  (ssize_t) (Rec2.RecordLength-i-2),exception);
1362              break;
1363
1364      case 0x1B:          /*bitmap rectangle*/
1365              WPG2Flags = LoadWPG2Flags(image,StartWPG.PosSizePrecision,NULL,&CTM);
1366              (void) WPG2Flags;
1367              break;
1368            }
1369        }
1370
1371      break;
1372
1373    default:
1374      {
1375         ThrowReaderException(CoderError,"DataEncodingSchemeIsNotSupported");
1376      }
1377   }
1378
1379 Finish:
1380  (void) CloseBlob(image);
1381
1382  {
1383    Image
1384      *p;
1385
1386    ssize_t
1387      scene=0;
1388
1389    /*
1390      Rewind list, removing any empty images while rewinding.
1391    */
1392    p=image;
1393    image=NULL;
1394    while (p != (Image *) NULL)
1395      {
1396        Image *tmp=p;
1397        if ((p->rows == 0) || (p->columns == 0)) {
1398          p=p->previous;
1399          DeleteImageFromList(&tmp);
1400        } else {
1401          image=p;
1402          p=p->previous;
1403        }
1404      }
1405    /*
1406      Fix scene numbers.
1407    */
1408    for (p=image; p != (Image *) NULL; p=p->next)
1409      p->scene=(size_t) scene++;
1410  }
1411  if (image == (Image *) NULL)
1412    ThrowReaderException(CorruptImageError,
1413      "ImageFileDoesNotContainAnyImageData");
1414  return(image);
1415}
1416
1417/*
1418%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1419%                                                                             %
1420%                                                                             %
1421%                                                                             %
1422%   R e g i s t e r W P G I m a g e                                           %
1423%                                                                             %
1424%                                                                             %
1425%                                                                             %
1426%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1427%
1428%  Method RegisterWPGImage adds attributes for the WPG image format to
1429%  the list of supported formats.  The attributes include the image format
1430%  tag, a method to read and/or write the format, whether the format
1431%  supports the saving of more than one frame to the same file or blob,
1432%  whether the format supports native in-memory I/O, and a brief
1433%  description of the format.
1434%
1435%  The format of the RegisterWPGImage method is:
1436%
1437%      size_t RegisterWPGImage(void)
1438%
1439*/
1440ModuleExport size_t RegisterWPGImage(void)
1441{
1442  MagickInfo
1443    *entry;
1444
1445  entry=SetMagickInfo("WPG");
1446  entry->decoder=(DecodeImageHandler *) ReadWPGImage;
1447  entry->magick=(IsImageFormatHandler *) IsWPG;
1448  entry->description=AcquireString("Word Perfect Graphics");
1449  entry->module=ConstantString("WPG");
1450  entry->seekable_stream=MagickTrue;
1451  (void) RegisterMagickInfo(entry);
1452  return(MagickImageCoderSignature);
1453}
1454
1455/*
1456%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1457%                                                                             %
1458%                                                                             %
1459%                                                                             %
1460%   U n r e g i s t e r W P G I m a g e                                       %
1461%                                                                             %
1462%                                                                             %
1463%                                                                             %
1464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465%
1466%  Method UnregisterWPGImage removes format registrations made by the
1467%  WPG module from the list of supported formats.
1468%
1469%  The format of the UnregisterWPGImage method is:
1470%
1471%      UnregisterWPGImage(void)
1472%
1473*/
1474ModuleExport void UnregisterWPGImage(void)
1475{
1476  (void) UnregisterMagickInfo("WPG");
1477}
1478