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