source: ImageMagick/trunk/coders/ps.c @ 8316

Revision 8316, 73.9 KB checked in by cristy, 11 months ago (diff)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                               PPPP   SSSSS                                  %
7%                               P   P  SS                                     %
8%                               PPPP    SSS                                   %
9%                               P         SS                                  %
10%                               P      SSSSS                                  %
11%                                                                             %
12%                                                                             %
13%                         Read/Write Postscript Format                        %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
50#include "MagickCore/colorspace-private.h"
51#include "MagickCore/constitute.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/delegate-private.h"
54#include "MagickCore/draw.h"
55#include "MagickCore/exception.h"
56#include "MagickCore/exception-private.h"
57#include "MagickCore/geometry.h"
58#include "MagickCore/image.h"
59#include "MagickCore/image-private.h"
60#include "MagickCore/list.h"
61#include "MagickCore/magick.h"
62#include "MagickCore/memory_.h"
63#include "MagickCore/monitor.h"
64#include "MagickCore/monitor-private.h"
65#include "MagickCore/option.h"
66#include "MagickCore/profile.h"
67#include "MagickCore/resource_.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum-private.h"
71#include "MagickCore/static.h"
72#include "MagickCore/string_.h"
73#include "MagickCore/module.h"
74#include "MagickCore/token.h"
75#include "MagickCore/transform.h"
76#include "MagickCore/utility.h"
77
78/*
79  Forward declarations.
80*/
81static MagickBooleanType
82  WritePSImage(const ImageInfo *,Image *,ExceptionInfo *);
83
84/*
85%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86%                                                                             %
87%                                                                             %
88%                                                                             %
89%   I n v o k e P o s t s r i p t D e l e g a t e                             %
90%                                                                             %
91%                                                                             %
92%                                                                             %
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94%
95%  InvokePostscriptDelegate() executes the Postscript interpreter with the
96%  specified command.
97%
98%  The format of the InvokePostscriptDelegate method is:
99%
100%      MagickBooleanType InvokePostscriptDelegate(
101%        const MagickBooleanType verbose,const char *command,
102%        ExceptionInfo *exception)
103%
104%  A description of each parameter follows:
105%
106%    o verbose: A value other than zero displays the command prior to
107%      executing it.
108%
109%    o command: the address of a character string containing the command to
110%      execute.
111%
112%    o exception: return any errors or warnings in this structure.
113%
114*/
115static MagickBooleanType InvokePostscriptDelegate(
116  const MagickBooleanType verbose,const char *command,ExceptionInfo *exception)
117{
118  int
119    status;
120
121#if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
122  char
123    **argv;
124
125  const GhostInfo
126    *ghost_info;
127
128  gs_main_instance
129    *interpreter;
130
131  int
132    argc,
133    code;
134
135  register ssize_t
136    i;
137
138#if defined(MAGICKCORE_WINDOWS_SUPPORT)
139  ghost_info=NTGhostscriptDLLVectors();
140#else
141  GhostInfo
142    ghost_info_struct;
143
144  ghost_info=(&ghost_info_struct);
145  (void) ResetMagickMemory(&ghost_info,0,sizeof(ghost_info));
146  ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
147    gsapi_new_instance;
148  ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
149    gsapi_init_with_args;
150  ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
151    int *)) gsapi_run_string;
152  ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
153    gsapi_delete_instance;
154  ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
155#endif
156  if (ghost_info == (GhostInfo *) NULL)
157    {
158      status=SystemCommand(MagickFalse,verbose,command,exception);
159      return(status == 0 ? MagickTrue : MagickFalse);
160    }
161  if (verbose != MagickFalse)
162    {
163      (void) fputs("[ghostscript library]",stdout);
164      (void) fputs(strchr(command,' '),stdout);
165    }
166  status=(ghost_info->new_instance)(&interpreter,(void *) NULL);
167  if (status < 0)
168    {
169      status=SystemCommand(MagickFalse,verbose,command,exception);
170      return(status == 0 ? MagickTrue : MagickFalse);
171    }
172  code=0;
173  argv=StringToArgv(command,&argc);
174  status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
175  if (status == 0)
176    status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
177      0,&code);
178  (ghost_info->exit)(interpreter);
179  (ghost_info->delete_instance)(interpreter);
180#if defined(MAGICKCORE_WINDOWS_SUPPORT)
181  NTGhostscriptUnLoadDLL();
182#endif
183  for (i=0; i < (ssize_t) argc; i++)
184    argv[i]=DestroyString(argv[i]);
185  argv=(char **) RelinquishMagickMemory(argv);
186  if ((status != 0) && (status != -101))
187    {
188      char
189        *message;
190
191      message=GetExceptionMessage(errno);
192      (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
193        "`%s': %s",command,message);
194      message=DestroyString(message);
195      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
196        "Ghostscript returns status %d, exit code %d",status,code);
197      return(MagickFalse);
198    }
199  return(MagickTrue);
200#else
201  status=SystemCommand(MagickFalse,verbose,command,exception);
202  return(status == 0 ? MagickTrue : MagickFalse);
203#endif
204}
205
206/*
207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208%                                                                             %
209%                                                                             %
210%                                                                             %
211%   I s P S                                                                   %
212%                                                                             %
213%                                                                             %
214%                                                                             %
215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216%
217%  IsPS() returns MagickTrue if the image format type, identified by the
218%  magick string, is PS.
219%
220%  The format of the IsPS method is:
221%
222%      MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
223%
224%  A description of each parameter follows:
225%
226%    o magick: compare image format pattern against these bytes.
227%
228%    o length: Specifies the length of the magick string.
229%
230*/
231static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
232{
233  if (length < 4)
234    return(MagickFalse);
235  if (memcmp(magick,"%!",2) == 0)
236    return(MagickTrue);
237  if (memcmp(magick,"\004%!",3) == 0)
238    return(MagickTrue);
239  return(MagickFalse);
240}
241
242/*
243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244%                                                                             %
245%                                                                             %
246%                                                                             %
247%   R e a d P S I m a g e                                                     %
248%                                                                             %
249%                                                                             %
250%                                                                             %
251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252%
253%  ReadPSImage() reads a Postscript image file and returns it.  It allocates
254%  the memory necessary for the new Image structure and returns a pointer
255%  to the new image.
256%
257%  The format of the ReadPSImage method is:
258%
259%      Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
260%
261%  A description of each parameter follows:
262%
263%    o image_info: the image info.
264%
265%    o exception: return any errors or warnings in this structure.
266%
267*/
268
269static MagickBooleanType IsPostscriptRendered(const char *path)
270{
271  MagickBooleanType
272    status;
273
274  struct stat
275    attributes;
276
277  if ((path == (const char *) NULL) || (*path == '\0'))
278    return(MagickFalse);
279  status=GetPathAttributes(path,&attributes);
280  if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
281      (attributes.st_size > 0))
282    return(MagickTrue);
283  return(MagickFalse);
284}
285
286static inline int ProfileInteger(Image *image,short int *hex_digits)
287{
288  int
289    c,
290    l,
291    value;
292
293  register ssize_t
294    i;
295
296  l=0;
297  value=0;
298  for (i=0; i < 2; )
299  {
300    c=ReadBlobByte(image);
301    if ((c == EOF) || ((c == '%') && (l == '%')))
302      {
303        value=(-1);
304        break;
305      }
306    l=c;
307    c&=0xff;
308    if (isxdigit(c) == MagickFalse)
309      continue;
310    value=(int) ((size_t) value << 4)+hex_digits[c];
311    i++;
312  }
313  return(value);
314}
315
316static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
317{
318#define BoundingBox  "BoundingBox:"
319#define BeginDocument  "BeginDocument:"
320#define BeginXMPPacket  "<?xpacket begin="
321#define EndXMPPacket  "<?xpacket end="
322#define ICCProfile "BeginICCProfile:"
323#define CMYKCustomColor  "CMYKCustomColor:"
324#define CMYKProcessColor  "CMYKProcessColor:"
325#define DocumentMedia  "DocumentMedia:"
326#define DocumentCustomColors  "DocumentCustomColors:"
327#define DocumentProcessColors  "DocumentProcessColors:"
328#define EndDocument  "EndDocument:"
329#define HiResBoundingBox  "HiResBoundingBox:"
330#define ImageData  "ImageData:"
331#define PageBoundingBox  "PageBoundingBox:"
332#define LanguageLevel  "LanguageLevel:"
333#define PageMedia  "PageMedia:"
334#define Pages  "Pages:"
335#define PhotoshopProfile  "BeginPhotoshop:"
336#define PostscriptLevel  "!PS-"
337#define RenderPostscriptText  "  Rendering Postscript...  "
338#define SpotColor  "+ "
339
340  char
341    command[MaxTextExtent],
342    density[MaxTextExtent],
343    filename[MaxTextExtent],
344    geometry[MaxTextExtent],
345    input_filename[MaxTextExtent],
346    options[MaxTextExtent],
347    postscript_filename[MaxTextExtent];
348
349  const DelegateInfo
350    *delegate_info;
351
352  GeometryInfo
353    geometry_info;
354
355  Image
356    *image,
357    *next,
358    *postscript_image;
359
360  ImageInfo
361    *read_info;
362
363  int
364    c,
365    file;
366
367  MagickBooleanType
368    cmyk,
369    skip,
370    status;
371
372  MagickStatusType
373    flags;
374
375  PointInfo
376    delta,
377    resolution;
378
379  RectangleInfo
380    page;
381
382  register char
383    *p;
384
385  register ssize_t
386    i;
387
388  SegmentInfo
389    bounds,
390    hires_bounds;
391
392  short int
393    hex_digits[256];
394
395  size_t
396    length,
397    priority;
398
399  ssize_t
400    count;
401
402  StringInfo
403    *profile;
404
405  unsigned long
406    columns,
407    extent,
408    language_level,
409    pages,
410    rows,
411    scene,
412    spotcolor;
413
414  /*
415    Open image file.
416  */
417  assert(image_info != (const ImageInfo *) NULL);
418  assert(image_info->signature == MagickSignature);
419  if (image_info->debug != MagickFalse)
420    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
421      image_info->filename);
422  assert(exception != (ExceptionInfo *) NULL);
423  assert(exception->signature == MagickSignature);
424  image=AcquireImage(image_info,exception);
425  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
426  if (status == MagickFalse)
427    {
428      image=DestroyImageList(image);
429      return((Image *) NULL);
430    }
431  status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
432  if (status == MagickFalse)
433    {
434      ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
435        image_info->filename);
436      image=DestroyImageList(image);
437      return((Image *) NULL);
438    }
439  /*
440    Initialize hex values.
441  */
442  (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
443  hex_digits[(int) '0']=0;
444  hex_digits[(int) '1']=1;
445  hex_digits[(int) '2']=2;
446  hex_digits[(int) '3']=3;
447  hex_digits[(int) '4']=4;
448  hex_digits[(int) '5']=5;
449  hex_digits[(int) '6']=6;
450  hex_digits[(int) '7']=7;
451  hex_digits[(int) '8']=8;
452  hex_digits[(int) '9']=9;
453  hex_digits[(int) 'a']=10;
454  hex_digits[(int) 'b']=11;
455  hex_digits[(int) 'c']=12;
456  hex_digits[(int) 'd']=13;
457  hex_digits[(int) 'e']=14;
458  hex_digits[(int) 'f']=15;
459  hex_digits[(int) 'A']=10;
460  hex_digits[(int) 'B']=11;
461  hex_digits[(int) 'C']=12;
462  hex_digits[(int) 'D']=13;
463  hex_digits[(int) 'E']=14;
464  hex_digits[(int) 'F']=15;
465  /*
466    Set the page density.
467  */
468  delta.x=DefaultResolution;
469  delta.y=DefaultResolution;
470  if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
471    {
472      flags=ParseGeometry(PSDensityGeometry,&geometry_info);
473      image->resolution.x=geometry_info.rho;
474      image->resolution.y=geometry_info.sigma;
475      if ((flags & SigmaValue) == 0)
476        image->resolution.y=image->resolution.x;
477    }
478  if (image_info->density != (char *) NULL)
479    {
480      flags=ParseGeometry(image_info->density,&geometry_info);
481      image->resolution.x=geometry_info.rho;
482      image->resolution.y=geometry_info.sigma;
483      if ((flags & SigmaValue) == 0)
484        image->resolution.y=image->resolution.x;
485    }
486  (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
487  if (image_info->page != (char *) NULL)
488    (void) ParseAbsoluteGeometry(image_info->page,&page);
489  resolution=image->resolution;
490  page.width=(size_t) ceil((double) (page.width*resolution.x/delta.x)-0.5);
491  page.height=(size_t) ceil((double) (page.height*resolution.y/delta.y)-0.5);
492  /*
493    Determine page geometry from the Postscript bounding box.
494  */
495  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
496  (void) ResetMagickMemory(command,0,sizeof(command));
497  cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
498  (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
499  priority=0;
500  rows=0;
501  extent=0;
502  spotcolor=0;
503  language_level=1;
504  pages=(~0UL);
505  skip=MagickFalse;
506  p=command;
507  for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
508  {
509    /*
510      Note document structuring comments.
511    */
512    *p++=(char) c;
513    if ((strchr("\n\r%",c) == (char *) NULL) &&
514        ((size_t) (p-command) < (MaxTextExtent-1)))
515      continue;
516    *p='\0';
517    p=command;
518    /*
519      Skip %%BeginDocument thru %%EndDocument.
520    */
521    if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
522      skip=MagickTrue;
523    if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
524      skip=MagickFalse;
525    if (skip != MagickFalse)
526      continue;
527    if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
528      {
529        (void) SetImageProperty(image,"ps:Level",command+4,exception);
530        if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
531          pages=1;
532      }
533    if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
534      (void) sscanf(command,LanguageLevel " %lu",&language_level);
535    if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
536      (void) sscanf(command,Pages " %lu",&pages);
537    if (LocaleNCompare(ImageData,command,strlen(ImageData)) == 0)
538      (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
539    if (LocaleNCompare(ICCProfile,command,strlen(ICCProfile)) == 0)
540      {
541        unsigned char
542          *datum;
543
544        /*
545          Read ICC profile.
546        */
547        profile=AcquireStringInfo(65536);
548        for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
549        {
550          SetStringInfoLength(profile,(size_t) i+1);
551          datum=GetStringInfoDatum(profile);
552          datum[i]=(unsigned char) c;
553        }
554        (void) SetImageProfile(image,"icc",profile,exception);
555        profile=DestroyStringInfo(profile);
556        continue;
557      }
558    if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
559      {
560        unsigned char
561          *p;
562
563        /*
564          Read Photoshop profile.
565        */
566        count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
567        if (count != 1)
568          continue;
569        length=extent;
570        profile=BlobToStringInfo((const void *) NULL,length);
571        p=GetStringInfoDatum(profile);
572        for (i=0; i < (ssize_t) length; i++)
573          *p++=(unsigned char) ProfileInteger(image,hex_digits);
574        (void) SetImageProfile(image,"8bim",profile,exception);
575        profile=DestroyStringInfo(profile);
576        continue;
577      }
578    if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
579      {
580        register size_t
581          i;
582
583        /*
584          Read XMP profile.
585        */
586        p=command;
587        profile=StringToStringInfo(command);
588        for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
589        {
590          SetStringInfoLength(profile,i+1);
591          c=ReadBlobByte(image);
592          GetStringInfoDatum(profile)[i]=(unsigned char) c;
593          *p++=(char) c;
594          if ((strchr("\n\r%",c) == (char *) NULL) &&
595              ((size_t) (p-command) < (MaxTextExtent-1)))
596            continue;
597          *p='\0';
598          p=command;
599          if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
600            break;
601        }
602        SetStringInfoLength(profile,i);
603        (void) SetImageProfile(image,"xmp",profile,exception);
604        profile=DestroyStringInfo(profile);
605        continue;
606      }
607    /*
608      Is this a CMYK document?
609    */
610    length=strlen(DocumentProcessColors);
611    if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
612      {
613        if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
614            (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
615            (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
616          cmyk=MagickTrue;
617      }
618    if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
619      cmyk=MagickTrue;
620    if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
621      cmyk=MagickTrue;
622    length=strlen(DocumentCustomColors);
623    if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
624        (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
625        (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
626      {
627        char
628          property[MaxTextExtent],
629          *value;
630
631        register char
632          *p;
633
634        /*
635          Note spot names.
636        */
637        (void) FormatLocaleString(property,MaxTextExtent,"ps:SpotColor-%.20g",
638          (double) (spotcolor++));
639        for (p=command; *p != '\0'; p++)
640          if (isspace((int) (unsigned char) *p) != 0)
641            break;
642        value=AcquireString(p);
643        (void) SubstituteString(&value,"(","");
644        (void) SubstituteString(&value,")","");
645        (void) StripString(value);
646        (void) SetImageProperty(image,property,value,exception);
647        value=DestroyString(value);
648        continue;
649      }
650    if (image_info->page != (char *) NULL)
651      continue;
652    /*
653      Note region defined by bounding box.
654    */
655    count=0;
656    i=0;
657    if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
658      {
659        count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",
660          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
661        i=2;
662      }
663    if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
664      {
665        count=(ssize_t) sscanf(command,DocumentMedia " %lf %lf %lf %lf",
666          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
667        i=1;
668      }
669    if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
670      {
671        count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
672          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
673        i=3;
674      }
675    if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
676      {
677        count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
678          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
679        i=1;
680      }
681    if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
682      {
683        count=(ssize_t) sscanf(command,PageMedia " %lf %lf %lf %lf",
684          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
685        i=1;
686      }
687    if ((count != 4) || (i < (ssize_t) priority))
688      continue;
689    if ((fabs(bounds.x2-bounds.x1) <= fabs(hires_bounds.x2-hires_bounds.x1)) ||
690        (fabs(bounds.y2-bounds.y1) <= fabs(hires_bounds.y2-hires_bounds.y1)))
691      continue;
692    hires_bounds=bounds;
693    priority=i;
694  }
695  if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) &&
696      (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
697    {
698      /*
699        Set Postscript render geometry.
700      */
701      (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g%+.15g%+.15g",
702        hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
703        hires_bounds.x1,hires_bounds.y1);
704      (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
705      page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
706        resolution.x/delta.x)-0.5);
707      page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
708        resolution.y/delta.y)-0.5);
709    }
710  (void) CloseBlob(image);
711  if (IssRGBColorspace(image_info->colorspace) != MagickFalse)
712    cmyk=MagickFalse;
713  /*
714    Create Ghostscript control file.
715  */
716  file=AcquireUniqueFileResource(postscript_filename);
717  if (file == -1)
718    {
719      ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
720        image_info->filename);
721      image=DestroyImageList(image);
722      return((Image *) NULL);
723    }
724  (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
725    "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
726    "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
727  count=write(file,command,(unsigned int) strlen(command));
728  if (image_info->page == (char *) NULL)
729    {
730      char
731        translate_geometry[MaxTextExtent];
732
733      (void) FormatLocaleString(translate_geometry,MaxTextExtent,
734        "%g %g translate\n",-bounds.x1,-bounds.y1);
735      count=write(file,translate_geometry,(unsigned int)
736        strlen(translate_geometry));
737    }
738  file=close(file)-1;
739  /*
740    Render Postscript with the Ghostscript delegate.
741  */
742  if (image_info->monochrome != MagickFalse)
743    delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
744  else
745    if (cmyk != MagickFalse)
746      delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
747    else
748      delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
749  if (delegate_info == (const DelegateInfo *) NULL)
750    {
751      (void) RelinquishUniqueFileResource(postscript_filename);
752      image=DestroyImageList(image);
753      return((Image *) NULL);
754    }
755  *options='\0';
756  (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",resolution.x,
757    resolution.y);
758  (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
759    page.width,(double) page.height);
760  read_info=CloneImageInfo(image_info);
761  *read_info->magick='\0';
762  if (read_info->number_scenes != 0)
763    {
764      char
765        pages[MaxTextExtent];
766
767      (void) FormatLocaleString(pages,MaxTextExtent,"-dFirstPage=%.20g "
768        "-dLastPage=%.20g",(double) read_info->scene+1,(double)
769        (read_info->scene+read_info->number_scenes));
770      (void) ConcatenateMagickString(options,pages,MaxTextExtent);
771      read_info->number_scenes=0;
772      if (read_info->scenes != (char *) NULL)
773        *read_info->scenes='\0';
774    }
775  if (IfMagickTrue(IsStringTrue(GetImageOption(image_info,"ps:use-cropbox"))))
776    (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
777  (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
778  (void) AcquireUniqueFilename(filename);
779  (void) ConcatenateMagickString(filename,"-%08d",MaxTextExtent);
780  (void) FormatLocaleString(command,MaxTextExtent,
781    GetDelegateCommands(delegate_info),
782    read_info->antialias != MagickFalse ? 4 : 1,
783    read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
784    postscript_filename,input_filename);
785  status=InvokePostscriptDelegate(read_info->verbose,command,exception);
786  (void) InterpretImageFilename(image_info,image,filename,1,
787    read_info->filename,exception);
788  if ((status == MagickFalse) ||
789      (IsPostscriptRendered(read_info->filename) == MagickFalse))
790    {
791      (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
792      status=InvokePostscriptDelegate(read_info->verbose,command,exception);
793    }
794  (void) RelinquishUniqueFileResource(postscript_filename);
795  (void) RelinquishUniqueFileResource(input_filename);
796  postscript_image=(Image *) NULL;
797  if (status == MagickFalse)
798    for (i=1; ; i++)
799    {
800      (void) InterpretImageFilename(image_info,image,filename,(int) i,
801        read_info->filename,exception);
802      if (IsPostscriptRendered(read_info->filename) == MagickFalse)
803        break;
804      (void) RelinquishUniqueFileResource(read_info->filename);
805    }
806  else
807    for (i=1; ; i++)
808    {
809      (void) InterpretImageFilename(image_info,image,filename,(int) i,
810        read_info->filename,exception);
811      if (IsPostscriptRendered(read_info->filename) == MagickFalse)
812        break;
813      next=ReadImage(read_info,exception);
814      (void) RelinquishUniqueFileResource(read_info->filename);
815      if (next == (Image *) NULL)
816        break;
817      AppendImageToList(&postscript_image,next);
818    }
819  (void) RelinquishUniqueFileResource(read_info->filename);
820  read_info=DestroyImageInfo(read_info);
821  if (postscript_image == (Image *) NULL)
822    {
823      image=DestroyImageList(image);
824      ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
825        image_info->filename);
826      return((Image *) NULL);
827    }
828  if (LocaleCompare(postscript_image->magick,"BMP") == 0)
829    {
830      Image
831        *cmyk_image;
832
833      cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
834      if (cmyk_image != (Image *) NULL)
835        {
836          postscript_image=DestroyImageList(postscript_image);
837          postscript_image=cmyk_image;
838        }
839    }
840  if (image_info->number_scenes != 0)
841    {
842      Image
843        *clone_image;
844
845      register ssize_t
846        i;
847
848      /*
849        Add place holder images to meet the subimage specification requirement.
850      */
851      for (i=0; i < (ssize_t) image_info->scene; i++)
852      {
853        clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
854        if (clone_image != (Image *) NULL)
855          PrependImageToList(&postscript_image,clone_image);
856      }
857    }
858  do
859  {
860    (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
861    if (columns != 0)
862      postscript_image->magick_columns=columns;
863    if (rows != 0)
864      postscript_image->magick_rows=rows;
865    postscript_image->page=page;
866    (void) CloneImageProfiles(postscript_image,image);
867    (void) CloneImageProperties(postscript_image,image);
868    next=SyncNextImageInList(postscript_image);
869    if (next != (Image *) NULL)
870      postscript_image=next;
871  } while (next != (Image *) NULL);
872  image=DestroyImageList(image);
873  scene=0;
874  for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
875  {
876    next->scene=scene++;
877    next=GetNextImageInList(next);
878  }
879  return(GetFirstImageInList(postscript_image));
880}
881
882/*
883%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
884%                                                                             %
885%                                                                             %
886%                                                                             %
887%   R e g i s t e r P S I m a g e                                             %
888%                                                                             %
889%                                                                             %
890%                                                                             %
891%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892%
893%  RegisterPSImage() adds properties for the PS image format to
894%  the list of supported formats.  The properties include the image format
895%  tag, a method to read and/or write the format, whether the format
896%  supports the saving of more than one frame to the same file or blob,
897%  whether the format supports native in-memory I/O, and a brief
898%  description of the format.
899%
900%  The format of the RegisterPSImage method is:
901%
902%      size_t RegisterPSImage(void)
903%
904*/
905ModuleExport size_t RegisterPSImage(void)
906{
907  MagickInfo
908    *entry;
909
910  entry=SetMagickInfo("EPI");
911  entry->decoder=(DecodeImageHandler *) ReadPSImage;
912  entry->encoder=(EncodeImageHandler *) WritePSImage;
913  entry->magick=(IsImageFormatHandler *) IsPS;
914  entry->adjoin=MagickFalse;
915  entry->blob_support=MagickFalse;
916  entry->seekable_stream=MagickTrue;
917  entry->thread_support=EncoderThreadSupport;
918  entry->description=ConstantString(
919   "Encapsulated PostScript Interchange format");
920  entry->module=ConstantString("PS");
921  (void) RegisterMagickInfo(entry);
922  entry=SetMagickInfo("EPS");
923  entry->decoder=(DecodeImageHandler *) ReadPSImage;
924  entry->encoder=(EncodeImageHandler *) WritePSImage;
925  entry->magick=(IsImageFormatHandler *) IsPS;
926  entry->adjoin=MagickFalse;
927  entry->blob_support=MagickFalse;
928  entry->seekable_stream=MagickTrue;
929  entry->thread_support=EncoderThreadSupport;
930  entry->description=ConstantString("Encapsulated PostScript");
931  entry->module=ConstantString("PS");
932  (void) RegisterMagickInfo(entry);
933  entry=SetMagickInfo("EPSF");
934  entry->decoder=(DecodeImageHandler *) ReadPSImage;
935  entry->encoder=(EncodeImageHandler *) WritePSImage;
936  entry->magick=(IsImageFormatHandler *) IsPS;
937  entry->adjoin=MagickFalse;
938  entry->blob_support=MagickFalse;
939  entry->seekable_stream=MagickTrue;
940  entry->description=ConstantString("Encapsulated PostScript");
941  entry->module=ConstantString("PS");
942  (void) RegisterMagickInfo(entry);
943  entry=SetMagickInfo("EPSI");
944  entry->decoder=(DecodeImageHandler *) ReadPSImage;
945  entry->encoder=(EncodeImageHandler *) WritePSImage;
946  entry->magick=(IsImageFormatHandler *) IsPS;
947  entry->adjoin=MagickFalse;
948  entry->blob_support=MagickFalse;
949  entry->seekable_stream=MagickTrue;
950  entry->thread_support=EncoderThreadSupport;
951  entry->description=ConstantString(
952    "Encapsulated PostScript Interchange format");
953  entry->module=ConstantString("PS");
954  (void) RegisterMagickInfo(entry);
955  entry=SetMagickInfo("PS");
956  entry->decoder=(DecodeImageHandler *) ReadPSImage;
957  entry->encoder=(EncodeImageHandler *) WritePSImage;
958  entry->magick=(IsImageFormatHandler *) IsPS;
959  entry->module=ConstantString("PS");
960  entry->blob_support=MagickFalse;
961  entry->seekable_stream=MagickTrue;
962  entry->thread_support=EncoderThreadSupport;
963  entry->description=ConstantString("PostScript");
964  (void) RegisterMagickInfo(entry);
965  return(MagickImageCoderSignature);
966}
967
968/*
969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970%                                                                             %
971%                                                                             %
972%                                                                             %
973%   U n r e g i s t e r P S I m a g e                                         %
974%                                                                             %
975%                                                                             %
976%                                                                             %
977%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
978%
979%  UnregisterPSImage() removes format registrations made by the
980%  PS module from the list of supported formats.
981%
982%  The format of the UnregisterPSImage method is:
983%
984%      UnregisterPSImage(void)
985%
986*/
987ModuleExport void UnregisterPSImage(void)
988{
989  (void) UnregisterMagickInfo("EPI");
990  (void) UnregisterMagickInfo("EPS");
991  (void) UnregisterMagickInfo("EPSF");
992  (void) UnregisterMagickInfo("EPSI");
993  (void) UnregisterMagickInfo("PS");
994}
995
996/*
997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998%                                                                             %
999%                                                                             %
1000%                                                                             %
1001%   W r i t e P S I m a g e                                                   %
1002%                                                                             %
1003%                                                                             %
1004%                                                                             %
1005%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1006%
1007%  WritePSImage translates an image to encapsulated Postscript
1008%  Level I for printing.  If the supplied geometry is null, the image is
1009%  centered on the Postscript page.  Otherwise, the image is positioned as
1010%  specified by the geometry.
1011%
1012%  The format of the WritePSImage method is:
1013%
1014%      MagickBooleanType WritePSImage(const ImageInfo *image_info,
1015%        Image *image,ExceptionInfo *exception)
1016%
1017%  A description of each parameter follows:
1018%
1019%    o image_info: the image info.
1020%
1021%    o image: the image.
1022%
1023%    o exception: return any errors or warnings in this structure.
1024%
1025*/
1026
1027static inline size_t MagickMin(const size_t x,const size_t y)
1028{
1029  if (x < y)
1030    return(x);
1031  return(y);
1032}
1033
1034static inline unsigned char *PopHexPixel(const char **hex_digits,
1035  const size_t pixel,unsigned char *pixels)
1036{
1037  register const char
1038    *hex;
1039
1040  hex=hex_digits[pixel];
1041  *pixels++=(unsigned char) (*hex++);
1042  *pixels++=(unsigned char) (*hex);
1043  return(pixels);
1044}
1045
1046static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1047  ExceptionInfo *exception)
1048{
1049#define WriteRunlengthPacket(image,pixel,length,p) \
1050{ \
1051  if ((image->matte != MagickFalse) && \
1052      (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
1053    { \
1054      q=PopHexPixel(hex_digits,0xff,q); \
1055      q=PopHexPixel(hex_digits,0xff,q); \
1056      q=PopHexPixel(hex_digits,0xff,q); \
1057    } \
1058  else \
1059    { \
1060      q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
1061      q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
1062      q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1063    } \
1064  q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
1065}
1066
1067  static const char
1068    *hex_digits[] =
1069    {
1070      "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1071      "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1072      "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1073      "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1074      "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1075      "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1076      "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1077      "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1078      "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1079      "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1080      "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1081      "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1082      "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1083      "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1084      "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1085      "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1086      "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1087      "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1088      "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1089      "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1090      "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1091      "FC", "FD", "FE", "FF",  (char *) NULL
1092    },
1093    *PostscriptProlog[]=
1094    {
1095      "%%BeginProlog",
1096      "%",
1097      "% Display a color image.  The image is displayed in color on",
1098      "% Postscript viewers or printers that support color, otherwise",
1099      "% it is displayed as grayscale.",
1100      "%",
1101      "/DirectClassPacket",
1102      "{",
1103      "  %",
1104      "  % Get a DirectClass packet.",
1105      "  %",
1106      "  % Parameters:",
1107      "  %   red.",
1108      "  %   green.",
1109      "  %   blue.",
1110      "  %   length: number of pixels minus one of this color (optional).",
1111      "  %",
1112      "  currentfile color_packet readhexstring pop pop",
1113      "  compression 0 eq",
1114      "  {",
1115      "    /number_pixels 3 def",
1116      "  }",
1117      "  {",
1118      "    currentfile byte readhexstring pop 0 get",
1119      "    /number_pixels exch 1 add 3 mul def",
1120      "  } ifelse",
1121      "  0 3 number_pixels 1 sub",
1122      "  {",
1123      "    pixels exch color_packet putinterval",
1124      "  } for",
1125      "  pixels 0 number_pixels getinterval",
1126      "} bind def",
1127      "",
1128      "/DirectClassImage",
1129      "{",
1130      "  %",
1131      "  % Display a DirectClass image.",
1132      "  %",
1133      "  systemdict /colorimage known",
1134      "  {",
1135      "    columns rows 8",
1136      "    [",
1137      "      columns 0 0",
1138      "      rows neg 0 rows",
1139      "    ]",
1140      "    { DirectClassPacket } false 3 colorimage",
1141      "  }",
1142      "  {",
1143      "    %",
1144      "    % No colorimage operator;  convert to grayscale.",
1145      "    %",
1146      "    columns rows 8",
1147      "    [",
1148      "      columns 0 0",
1149      "      rows neg 0 rows",
1150      "    ]",
1151      "    { GrayDirectClassPacket } image",
1152      "  } ifelse",
1153      "} bind def",
1154      "",
1155      "/GrayDirectClassPacket",
1156      "{",
1157      "  %",
1158      "  % Get a DirectClass packet;  convert to grayscale.",
1159      "  %",
1160      "  % Parameters:",
1161      "  %   red",
1162      "  %   green",
1163      "  %   blue",
1164      "  %   length: number of pixels minus one of this color (optional).",
1165      "  %",
1166      "  currentfile color_packet readhexstring pop pop",
1167      "  color_packet 0 get 0.299 mul",
1168      "  color_packet 1 get 0.587 mul add",
1169      "  color_packet 2 get 0.114 mul add",
1170      "  cvi",
1171      "  /gray_packet exch def",
1172      "  compression 0 eq",
1173      "  {",
1174      "    /number_pixels 1 def",
1175      "  }",
1176      "  {",
1177      "    currentfile byte readhexstring pop 0 get",
1178      "    /number_pixels exch 1 add def",
1179      "  } ifelse",
1180      "  0 1 number_pixels 1 sub",
1181      "  {",
1182      "    pixels exch gray_packet put",
1183      "  } for",
1184      "  pixels 0 number_pixels getinterval",
1185      "} bind def",
1186      "",
1187      "/GrayPseudoClassPacket",
1188      "{",
1189      "  %",
1190      "  % Get a PseudoClass packet;  convert to grayscale.",
1191      "  %",
1192      "  % Parameters:",
1193      "  %   index: index into the colormap.",
1194      "  %   length: number of pixels minus one of this color (optional).",
1195      "  %",
1196      "  currentfile byte readhexstring pop 0 get",
1197      "  /offset exch 3 mul def",
1198      "  /color_packet colormap offset 3 getinterval def",
1199      "  color_packet 0 get 0.299 mul",
1200      "  color_packet 1 get 0.587 mul add",
1201      "  color_packet 2 get 0.114 mul add",
1202      "  cvi",
1203      "  /gray_packet exch def",
1204      "  compression 0 eq",
1205      "  {",
1206      "    /number_pixels 1 def",
1207      "  }",
1208      "  {",
1209      "    currentfile byte readhexstring pop 0 get",
1210      "    /number_pixels exch 1 add def",
1211      "  } ifelse",
1212      "  0 1 number_pixels 1 sub",
1213      "  {",
1214      "    pixels exch gray_packet put",
1215      "  } for",
1216      "  pixels 0 number_pixels getinterval",
1217      "} bind def",
1218      "",
1219      "/PseudoClassPacket",
1220      "{",
1221      "  %",
1222      "  % Get a PseudoClass packet.",
1223      "  %",
1224      "  % Parameters:",
1225      "  %   index: index into the colormap.",
1226      "  %   length: number of pixels minus one of this color (optional).",
1227      "  %",
1228      "  currentfile byte readhexstring pop 0 get",
1229      "  /offset exch 3 mul def",
1230      "  /color_packet colormap offset 3 getinterval def",
1231      "  compression 0 eq",
1232      "  {",
1233      "    /number_pixels 3 def",
1234      "  }",
1235      "  {",
1236      "    currentfile byte readhexstring pop 0 get",
1237      "    /number_pixels exch 1 add 3 mul def",
1238      "  } ifelse",
1239      "  0 3 number_pixels 1 sub",
1240      "  {",
1241      "    pixels exch color_packet putinterval",
1242      "  } for",
1243      "  pixels 0 number_pixels getinterval",
1244      "} bind def",
1245      "",
1246      "/PseudoClassImage",
1247      "{",
1248      "  %",
1249      "  % Display a PseudoClass image.",
1250      "  %",
1251      "  % Parameters:",
1252      "  %   class: 0-PseudoClass or 1-Grayscale.",
1253      "  %",
1254      "  currentfile buffer readline pop",
1255      "  token pop /class exch def pop",
1256      "  class 0 gt",
1257      "  {",
1258      "    currentfile buffer readline pop",
1259      "    token pop /depth exch def pop",
1260      "    /grays columns 8 add depth sub depth mul 8 idiv string def",
1261      "    columns rows depth",
1262      "    [",
1263      "      columns 0 0",
1264      "      rows neg 0 rows",
1265      "    ]",
1266      "    { currentfile grays readhexstring pop } image",
1267      "  }",
1268      "  {",
1269      "    %",
1270      "    % Parameters:",
1271      "    %   colors: number of colors in the colormap.",
1272      "    %   colormap: red, green, blue color packets.",
1273      "    %",
1274      "    currentfile buffer readline pop",
1275      "    token pop /colors exch def pop",
1276      "    /colors colors 3 mul def",
1277      "    /colormap colors string def",
1278      "    currentfile colormap readhexstring pop pop",
1279      "    systemdict /colorimage known",
1280      "    {",
1281      "      columns rows 8",
1282      "      [",
1283      "        columns 0 0",
1284      "        rows neg 0 rows",
1285      "      ]",
1286      "      { PseudoClassPacket } false 3 colorimage",
1287      "    }",
1288      "    {",
1289      "      %",
1290      "      % No colorimage operator;  convert to grayscale.",
1291      "      %",
1292      "      columns rows 8",
1293      "      [",
1294      "        columns 0 0",
1295      "        rows neg 0 rows",
1296      "      ]",
1297      "      { GrayPseudoClassPacket } image",
1298      "    } ifelse",
1299      "  } ifelse",
1300      "} bind def",
1301      "",
1302      "/DisplayImage",
1303      "{",
1304      "  %",
1305      "  % Display a DirectClass or PseudoClass image.",
1306      "  %",
1307      "  % Parameters:",
1308      "  %   x & y translation.",
1309      "  %   x & y scale.",
1310      "  %   label pointsize.",
1311      "  %   image label.",
1312      "  %   image columns & rows.",
1313      "  %   class: 0-DirectClass or 1-PseudoClass.",
1314      "  %   compression: 0-none or 1-RunlengthEncoded.",
1315      "  %   hex color packets.",
1316      "  %",
1317      "  gsave",
1318      "  /buffer 512 string def",
1319      "  /byte 1 string def",
1320      "  /color_packet 3 string def",
1321      "  /pixels 768 string def",
1322      "",
1323      "  currentfile buffer readline pop",
1324      "  token pop /x exch def",
1325      "  token pop /y exch def pop",
1326      "  x y translate",
1327      "  currentfile buffer readline pop",
1328      "  token pop /x exch def",
1329      "  token pop /y exch def pop",
1330      "  currentfile buffer readline pop",
1331      "  token pop /pointsize exch def pop",
1332      "  /Times-Roman findfont pointsize scalefont setfont",
1333      (char *) NULL
1334    },
1335    *PostscriptEpilog[]=
1336    {
1337      "  x y scale",
1338      "  currentfile buffer readline pop",
1339      "  token pop /columns exch def",
1340      "  token pop /rows exch def pop",
1341      "  currentfile buffer readline pop",
1342      "  token pop /class exch def pop",
1343      "  currentfile buffer readline pop",
1344      "  token pop /compression exch def pop",
1345      "  class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1346      "  grestore",
1347      (char *) NULL
1348    };
1349
1350  char
1351    buffer[MaxTextExtent],
1352    date[MaxTextExtent],
1353    **labels,
1354    page_geometry[MaxTextExtent];
1355
1356  const char
1357    **s,
1358    *value;
1359
1360  const StringInfo
1361    *profile;
1362
1363  double
1364    pointsize;
1365
1366  GeometryInfo
1367    geometry_info;
1368
1369  MagickBooleanType
1370    status;
1371
1372  MagickOffsetType
1373    scene;
1374
1375  MagickStatusType
1376    flags;
1377
1378  PixelInfo
1379    pixel;
1380
1381  PointInfo
1382    delta,
1383    resolution,
1384    scale;
1385
1386  Quantum
1387    index;
1388
1389  RectangleInfo
1390    geometry,
1391    media_info,
1392    page_info;
1393
1394  register const Quantum
1395    *p;
1396
1397  register ssize_t
1398    i,
1399    x;
1400
1401  register unsigned char
1402    *q;
1403
1404  SegmentInfo
1405    bounds;
1406
1407  size_t
1408    bit,
1409    byte,
1410    length,
1411    page,
1412    text_size;
1413
1414  ssize_t
1415    j,
1416    y;
1417
1418  time_t
1419    timer;
1420
1421  unsigned char
1422    pixels[2048];
1423
1424  /*
1425    Open output image file.
1426  */
1427  assert(image_info != (const ImageInfo *) NULL);
1428  assert(image_info->signature == MagickSignature);
1429  assert(image != (Image *) NULL);
1430  assert(image->signature == MagickSignature);
1431  if (image->debug != MagickFalse)
1432    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1433  assert(exception != (ExceptionInfo *) NULL);
1434  assert(exception->signature == MagickSignature);
1435  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1436  if (status == MagickFalse)
1437    return(status);
1438  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1439  page=1;
1440  scene=0;
1441  do
1442  {
1443    /*
1444      Scale relative to dots-per-inch.
1445    */
1446    if ((IssRGBColorspace(image->colorspace) == MagickFalse) &&
1447        (IsImageGray(image,exception) == MagickFalse) &&
1448        (image->colorspace != CMYKColorspace))
1449      (void) TransformImageColorspace(image,sRGBColorspace,exception);
1450    delta.x=DefaultResolution;
1451    delta.y=DefaultResolution;
1452    resolution.x=image->resolution.x;
1453    resolution.y=image->resolution.y;
1454    if ((resolution.x == 0.0) || (resolution.y == 0.0))
1455      {
1456        flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1457        resolution.x=geometry_info.rho;
1458        resolution.y=geometry_info.sigma;
1459        if ((flags & SigmaValue) == 0)
1460          resolution.y=resolution.x;
1461      }
1462    if (image_info->density != (char *) NULL)
1463      {
1464        flags=ParseGeometry(image_info->density,&geometry_info);
1465        resolution.x=geometry_info.rho;
1466        resolution.y=geometry_info.sigma;
1467        if ((flags & SigmaValue) == 0)
1468          resolution.y=resolution.x;
1469      }
1470    if (image->units == PixelsPerCentimeterResolution)
1471      {
1472        resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1473        resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
1474      }
1475    SetGeometry(image,&geometry);
1476    (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
1477      (double) image->columns,(double) image->rows);
1478    if (image_info->page != (char *) NULL)
1479      (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1480    else
1481      if ((image->page.width != 0) && (image->page.height != 0))
1482        (void) FormatLocaleString(page_geometry,MaxTextExtent,
1483          "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
1484          image->page.height,(double) image->page.x,(double) image->page.y);
1485      else
1486        if ((image->gravity != UndefinedGravity) &&
1487            (LocaleCompare(image_info->magick,"PS") == 0))
1488          (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1489    (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1490    (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1491      &geometry.width,&geometry.height);
1492    scale.x=(double) (geometry.width*delta.x)/resolution.x;
1493    geometry.width=(size_t) floor(scale.x+0.5);
1494    scale.y=(double) (geometry.height*delta.y)/resolution.y;
1495    geometry.height=(size_t) floor(scale.y+0.5);
1496    (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1497    (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
1498    if (image->gravity != UndefinedGravity)
1499      {
1500        geometry.x=(-page_info.x);
1501        geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
1502      }
1503    pointsize=12.0;
1504    if (image_info->pointsize != 0.0)
1505      pointsize=image_info->pointsize;
1506    text_size=0;
1507    value=GetImageProperty(image,"label",exception);
1508    if (value != (const char *) NULL)
1509      text_size=(size_t) (MultilineCensus(value)*pointsize+12);
1510    if (page == 1)
1511      {
1512        /*
1513          Output Postscript header.
1514        */
1515        if (LocaleCompare(image_info->magick,"PS") == 0)
1516          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1517        else
1518          (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1519            MaxTextExtent);
1520        (void) WriteBlobString(image,buffer);
1521        (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
1522        (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
1523          image->filename);
1524        (void) WriteBlobString(image,buffer);
1525        timer=time((time_t *) NULL);
1526        (void) FormatMagickTime(timer,MaxTextExtent,date);
1527        (void) FormatLocaleString(buffer,MaxTextExtent,
1528          "%%%%CreationDate: (%s)\n",date);
1529        (void) WriteBlobString(image,buffer);
1530        bounds.x1=(double) geometry.x;
1531        bounds.y1=(double) geometry.y;
1532        bounds.x2=(double) geometry.x+scale.x;
1533        bounds.y2=(double) geometry.y+(geometry.height+text_size);
1534        if ((image_info->adjoin != MagickFalse) &&
1535            (GetNextImageInList(image) != (Image *) NULL))
1536          (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1537            MaxTextExtent);
1538        else
1539          {
1540            (void) FormatLocaleString(buffer,MaxTextExtent,
1541              "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1542              ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
1543            (void) WriteBlobString(image,buffer);
1544            (void) FormatLocaleString(buffer,MaxTextExtent,
1545              "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
1546              bounds.y1,bounds.x2,bounds.y2);
1547          }
1548        (void) WriteBlobString(image,buffer);
1549        profile=GetImageProfile(image,"8bim");
1550        if (profile != (StringInfo *) NULL)
1551          {
1552            /*
1553              Embed Photoshop profile.
1554            */
1555            (void) FormatLocaleString(buffer,MaxTextExtent,
1556              "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
1557            (void) WriteBlobString(image,buffer);
1558            for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1559            {
1560              if ((i % 32) == 0)
1561                (void) WriteBlobString(image,"\n% ");
1562              (void) FormatLocaleString(buffer,MaxTextExtent,"%02X",
1563                (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1564              (void) WriteBlobString(image,buffer);
1565            }
1566            (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1567          }
1568        profile=GetImageProfile(image,"xmp");
1569        if (0 && (profile != (StringInfo *) NULL))
1570          {
1571            /*
1572              Embed XML profile.
1573            */
1574            (void) WriteBlobString(image,"\n%begin_xml_code\n");
1575            (void) FormatLocaleString(buffer,MaxTextExtent,
1576               "\n%%begin_xml_packet: %.20g\n",(double)
1577               GetStringInfoLength(profile));
1578            (void) WriteBlobString(image,buffer);
1579            for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1580              (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
1581            (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
1582          }
1583        value=GetImageProperty(image,"label",exception);
1584        if (value != (const char *) NULL)
1585          (void) WriteBlobString(image,
1586            "%%DocumentNeededResources: font Times-Roman\n");
1587        (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1588        (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1589        if (LocaleCompare(image_info->magick,"PS") != 0)
1590          (void) WriteBlobString(image,"%%Pages: 1\n");
1591        else
1592          {
1593            /*
1594              Compute the number of pages.
1595            */
1596            (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1597            (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1598            (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
1599              image_info->adjoin != MagickFalse ? (double)
1600              GetImageListLength(image) : 1.0);
1601            (void) WriteBlobString(image,buffer);
1602          }
1603        (void) WriteBlobString(image,"%%EndComments\n");
1604        (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1605        (void) WriteBlobString(image,"%%EndDefaults\n\n");
1606        if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1607            (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1608            (LocaleCompare(image_info->magick,"EPT") == 0))
1609          {
1610            Image
1611              *preview_image;
1612
1613            Quantum
1614              pixel;
1615
1616            register ssize_t
1617              x;
1618
1619            ssize_t
1620              y;
1621
1622            /*
1623              Create preview image.
1624            */
1625            preview_image=CloneImage(image,0,0,MagickTrue,exception);
1626            if (preview_image == (Image *) NULL)
1627              ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1628            /*
1629              Dump image as bitmap.
1630            */
1631            (void) FormatLocaleString(buffer,MaxTextExtent,
1632              "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%%  ",(double)
1633              preview_image->columns,(double) preview_image->rows,1.0,
1634              (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1635              35)/36));
1636            (void) WriteBlobString(image,buffer);
1637            q=pixels;
1638            for (y=0; y < (ssize_t) image->rows; y++)
1639            {
1640              p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1641                exception);
1642              if (p == (const Quantum *) NULL)
1643                break;
1644              bit=0;
1645              byte=0;
1646              for (x=0; x < (ssize_t) preview_image->columns; x++)
1647              {
1648                byte<<=1;
1649                pixel=GetPixelIntensity(preview_image,p);
1650                if (pixel >= (Quantum) (QuantumRange/2))
1651                  byte|=0x01;
1652                bit++;
1653                if (bit == 8)
1654                  {
1655                    q=PopHexPixel(hex_digits,byte,q);
1656                    if ((q-pixels+8) >= 80)
1657                      {
1658                        *q++='\n';
1659                        (void) WriteBlob(image,q-pixels,pixels);
1660                        q=pixels;
1661                        (void) WriteBlobString(image,"%  ");
1662                      };
1663                    bit=0;
1664                    byte=0;
1665                  }
1666              }
1667              if (bit != 0)
1668                {
1669                  byte<<=(8-bit);
1670                  q=PopHexPixel(hex_digits,byte,q);
1671                  if ((q-pixels+8) >= 80)
1672                    {
1673                      *q++='\n';
1674                      (void) WriteBlob(image,q-pixels,pixels);
1675                      q=pixels;
1676                      (void) WriteBlobString(image,"%  ");
1677                    };
1678                };
1679            }
1680            if (q != pixels)
1681              {
1682                *q++='\n';
1683                (void) WriteBlob(image,q-pixels,pixels);
1684              }
1685            (void) WriteBlobString(image,"\n%%EndPreview\n");
1686            preview_image=DestroyImage(preview_image);
1687          }
1688        /*
1689          Output Postscript commands.
1690        */
1691        for (s=PostscriptProlog; *s != (char *) NULL; s++)
1692        {
1693          (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
1694          (void) WriteBlobString(image,buffer);
1695        }
1696        value=GetImageProperty(image,"label",exception);
1697        if (value != (const char *) NULL)
1698          for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
1699          {
1700            (void) WriteBlobString(image,"  /label 512 string def\n");
1701            (void) WriteBlobString(image,"  currentfile label readline pop\n");
1702            (void) FormatLocaleString(buffer,MaxTextExtent,
1703              "  0 y %g add moveto label show pop\n",j*pointsize+12);
1704            (void) WriteBlobString(image,buffer);
1705          }
1706        for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1707        {
1708          (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
1709          (void) WriteBlobString(image,buffer);
1710        }
1711        if (LocaleCompare(image_info->magick,"PS") == 0)
1712          (void) WriteBlobString(image,"  showpage\n");
1713        (void) WriteBlobString(image,"} bind def\n");
1714        (void) WriteBlobString(image,"%%EndProlog\n");
1715      }
1716    (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page:  1 %.20g\n",
1717      (double) (page++));
1718    (void) WriteBlobString(image,buffer);
1719    (void) FormatLocaleString(buffer,MaxTextExtent,
1720      "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1721      (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
1722      (geometry.height+text_size));
1723    (void) WriteBlobString(image,buffer);
1724    if ((double) geometry.x < bounds.x1)
1725      bounds.x1=(double) geometry.x;
1726    if ((double) geometry.y < bounds.y1)
1727      bounds.y1=(double) geometry.y;
1728    if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1729      bounds.x2=(double) geometry.x+geometry.width-1;
1730    if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1731      bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1732    value=GetImageProperty(image,"label",exception);
1733    if (value != (const char *) NULL)
1734      (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1735    if (LocaleCompare(image_info->magick,"PS") != 0)
1736      (void) WriteBlobString(image,"userdict begin\n");
1737    (void) WriteBlobString(image,"DisplayImage\n");
1738    /*
1739      Output image data.
1740    */
1741    (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
1742      (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
1743    (void) WriteBlobString(image,buffer);
1744    labels=(char **) NULL;
1745    value=GetImageProperty(image,"label",exception);
1746    if (value != (const char *) NULL)
1747      labels=StringToList(value);
1748    if (labels != (char **) NULL)
1749      {
1750        for (i=0; labels[i] != (char *) NULL; i++)
1751        {
1752          (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
1753            labels[i]);
1754          (void) WriteBlobString(image,buffer);
1755          labels[i]=DestroyString(labels[i]);
1756        }
1757        labels=(char **) RelinquishMagickMemory(labels);
1758      }
1759    (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1760    pixel.alpha=(Quantum) TransparentAlpha;
1761    index=0;
1762    x=0;
1763    if ((image_info->type != TrueColorType) &&
1764        (IsImageGray(image,exception) != MagickFalse))
1765      {
1766        if (IsImageMonochrome(image,exception) == MagickFalse)
1767          {
1768            Quantum
1769              pixel;
1770
1771            /*
1772              Dump image as grayscale.
1773            */
1774            (void) FormatLocaleString(buffer,MaxTextExtent,
1775              "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1776              image->rows);
1777            (void) WriteBlobString(image,buffer);
1778            q=pixels;
1779            for (y=0; y < (ssize_t) image->rows; y++)
1780            {
1781              p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1782              if (p == (const Quantum *) NULL)
1783                break;
1784              for (x=0; x < (ssize_t) image->columns; x++)
1785              {
1786                pixel=(Quantum) ScaleQuantumToChar(GetPixelIntensity(image,p));
1787                q=PopHexPixel(hex_digits,(size_t) pixel,q);
1788                i++;
1789                if ((q-pixels+8) >= 80)
1790                  {
1791                    *q++='\n';
1792                    (void) WriteBlob(image,q-pixels,pixels);
1793                    q=pixels;
1794                  }
1795                p+=GetPixelChannels(image);
1796              }
1797              if (image->previous == (Image *) NULL)
1798                {
1799                  status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1800                    y,image->rows);
1801                  if (status == MagickFalse)
1802                    break;
1803                }
1804            }
1805            if (q != pixels)
1806              {
1807                *q++='\n';
1808                (void) WriteBlob(image,q-pixels,pixels);
1809              }
1810          }
1811        else
1812          {
1813            ssize_t
1814              y;
1815
1816            Quantum
1817              pixel;
1818
1819            /*
1820              Dump image as bitmap.
1821            */
1822            (void) FormatLocaleString(buffer,MaxTextExtent,
1823              "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1824              image->rows);
1825            (void) WriteBlobString(image,buffer);
1826            q=pixels;
1827            for (y=0; y < (ssize_t) image->rows; y++)
1828            {
1829              p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1830              if (p == (const Quantum *) NULL)
1831                break;
1832              bit=0;
1833              byte=0;
1834              for (x=0; x < (ssize_t) image->columns; x++)
1835              {
1836                byte<<=1;
1837                pixel=GetPixelIntensity(image,p);
1838                if (pixel >= (Quantum) (QuantumRange/2))
1839                  byte|=0x01;
1840                bit++;
1841                if (bit == 8)
1842                  {
1843                    q=PopHexPixel(hex_digits,byte,q);
1844                    if ((q-pixels+2) >= 80)
1845                      {
1846                        *q++='\n';
1847                        (void) WriteBlob(image,q-pixels,pixels);
1848                        q=pixels;
1849                      };
1850                    bit=0;
1851                    byte=0;
1852                  }
1853                p+=GetPixelChannels(image);
1854              }
1855              if (bit != 0)
1856                {
1857                  byte<<=(8-bit);
1858                  q=PopHexPixel(hex_digits,byte,q);
1859                  if ((q-pixels+2) >= 80)
1860                    {
1861                      *q++='\n';
1862                      (void) WriteBlob(image,q-pixels,pixels);
1863                      q=pixels;
1864                    }
1865                };
1866              if (image->previous == (Image *) NULL)
1867                {
1868                  status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1869                    y,image->rows);
1870                  if (status == MagickFalse)
1871                    break;
1872                }
1873            }
1874            if (q != pixels)
1875              {
1876                *q++='\n';
1877                (void) WriteBlob(image,q-pixels,pixels);
1878              }
1879          }
1880      }
1881    else
1882      if ((image->storage_class == DirectClass) ||
1883          (image->colors > 256) || (image->matte != MagickFalse))
1884        {
1885          /*
1886            Dump DirectClass image.
1887          */
1888          (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
1889            (double) image->columns,(double) image->rows,
1890            image_info->compression == RLECompression ? 1 : 0);
1891          (void) WriteBlobString(image,buffer);
1892          switch (image_info->compression)
1893          {
1894            case RLECompression:
1895            {
1896              /*
1897                Dump runlength-encoded DirectColor packets.
1898              */
1899              q=pixels;
1900              for (y=0; y < (ssize_t) image->rows; y++)
1901              {
1902                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1903                if (p == (const Quantum *) NULL)
1904                  break;
1905                GetPixelInfoPixel(image,p,&pixel);
1906                length=255;
1907                for (x=0; x < (ssize_t) image->columns; x++)
1908                {
1909                  if ((GetPixelRed(image,p) == pixel.red) &&
1910                      (GetPixelGreen(image,p) == pixel.green) &&
1911                      (GetPixelBlue(image,p) == pixel.blue) &&
1912                      (GetPixelAlpha(image,p) == pixel.alpha) &&
1913                      (length < 255) && (x < (ssize_t) (image->columns-1)))
1914                    length++;
1915                  else
1916                    {
1917                      if (x > 0)
1918                        {
1919                          WriteRunlengthPacket(image,pixel,length,p);
1920                          if ((q-pixels+10) >= 80)
1921                            {
1922                              *q++='\n';
1923                              (void) WriteBlob(image,q-pixels,pixels);
1924                              q=pixels;
1925                            }
1926                        }
1927                      length=0;
1928                    }
1929                  GetPixelInfoPixel(image,p,&pixel);
1930                  p+=GetPixelChannels(image);
1931                }
1932                WriteRunlengthPacket(image,pixel,length,p);
1933                if ((q-pixels+10) >= 80)
1934                  {
1935                    *q++='\n';
1936                    (void) WriteBlob(image,q-pixels,pixels);
1937                    q=pixels;
1938                  }
1939                if (image->previous == (Image *) NULL)
1940                  {
1941                    status=SetImageProgress(image,SaveImageTag,
1942                      (MagickOffsetType) y,image->rows);
1943                    if (status == MagickFalse)
1944                      break;
1945                  }
1946              }
1947              if (q != pixels)
1948                {
1949                  *q++='\n';
1950                  (void) WriteBlob(image,q-pixels,pixels);
1951                }
1952              break;
1953            }
1954            case NoCompression:
1955            default:
1956            {
1957              /*
1958                Dump uncompressed DirectColor packets.
1959              */
1960              q=pixels;
1961              for (y=0; y < (ssize_t) image->rows; y++)
1962              {
1963                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1964                if (p == (const Quantum *) NULL)
1965                  break;
1966                for (x=0; x < (ssize_t) image->columns; x++)
1967                {
1968                  if ((image->matte != MagickFalse) &&
1969                      (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
1970                    {
1971                      q=PopHexPixel(hex_digits,0xff,q);
1972                      q=PopHexPixel(hex_digits,0xff,q);
1973                      q=PopHexPixel(hex_digits,0xff,q);
1974                    }
1975                  else
1976                    {
1977                      q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1978                        GetPixelRed(image,p)),q);
1979                      q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1980                        GetPixelGreen(image,p)),q);
1981                      q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1982                        GetPixelBlue(image,p)),q);
1983                    }
1984                  if ((q-pixels+6) >= 80)
1985                    {
1986                      *q++='\n';
1987                      (void) WriteBlob(image,q-pixels,pixels);
1988                      q=pixels;
1989                    }
1990                  p+=GetPixelChannels(image);
1991                }
1992                if (image->previous == (Image *) NULL)
1993                  {
1994                    status=SetImageProgress(image,SaveImageTag,
1995                      (MagickOffsetType) y,image->rows);
1996                    if (status == MagickFalse)
1997                      break;
1998                  }
1999              }
2000              if (q != pixels)
2001                {
2002                  *q++='\n';
2003                  (void) WriteBlob(image,q-pixels,pixels);
2004                }
2005              break;
2006            }
2007          }
2008          (void) WriteBlobByte(image,'\n');
2009        }
2010      else
2011        {
2012          /*
2013            Dump PseudoClass image.
2014          */
2015          (void) FormatLocaleString(buffer,MaxTextExtent,
2016            "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2017            image->rows,image->storage_class == PseudoClass ? 1 : 0,
2018            image_info->compression == RLECompression ? 1 : 0);
2019          (void) WriteBlobString(image,buffer);
2020          /*
2021            Dump number of colors and colormap.
2022          */
2023          (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
2024            image->colors);
2025          (void) WriteBlobString(image,buffer);
2026          for (i=0; i < (ssize_t) image->colors; i++)
2027          {
2028            (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
2029              ScaleQuantumToChar(image->colormap[i].red),
2030              ScaleQuantumToChar(image->colormap[i].green),
2031              ScaleQuantumToChar(image->colormap[i].blue));
2032            (void) WriteBlobString(image,buffer);
2033          }
2034          switch (image_info->compression)
2035          {
2036            case RLECompression:
2037            {
2038              /*
2039                Dump runlength-encoded PseudoColor packets.
2040              */
2041              q=pixels;
2042              for (y=0; y < (ssize_t) image->rows; y++)
2043              {
2044                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2045                if (p == (const Quantum *) NULL)
2046                  break;
2047                index=GetPixelIndex(image,p);
2048                length=255;
2049                for (x=0; x < (ssize_t) image->columns; x++)
2050                {
2051                  if ((index == GetPixelIndex(image,p)) &&
2052                      (length < 255) && (x < ((ssize_t) image->columns-1)))
2053                    length++;
2054                  else
2055                    {
2056                      if (x > 0)
2057                        {
2058                          q=PopHexPixel(hex_digits,(size_t) index,q);
2059                          q=PopHexPixel(hex_digits,(size_t)
2060                            MagickMin(length,0xff),q);
2061                          i++;
2062                          if ((q-pixels+6) >= 80)
2063                            {
2064                              *q++='\n';
2065                              (void) WriteBlob(image,q-pixels,pixels);
2066                              q=pixels;
2067                            }
2068                        }
2069                      length=0;
2070                    }
2071                  index=GetPixelIndex(image,p);
2072                  pixel.red=GetPixelRed(image,p);
2073                  pixel.green=GetPixelGreen(image,p);
2074                  pixel.blue=GetPixelBlue(image,p);
2075                  pixel.alpha=GetPixelAlpha(image,p);
2076                  p+=GetPixelChannels(image);
2077                }
2078                q=PopHexPixel(hex_digits,(size_t) index,q);
2079                q=PopHexPixel(hex_digits,(size_t)
2080                  MagickMin(length,0xff),q);
2081                if (image->previous == (Image *) NULL)
2082                  {
2083                    status=SetImageProgress(image,SaveImageTag,
2084                      (MagickOffsetType) y,image->rows);
2085                    if (status == MagickFalse)
2086                      break;
2087                  }
2088              }
2089              if (q != pixels)
2090                {
2091                  *q++='\n';
2092                  (void) WriteBlob(image,q-pixels,pixels);
2093                }
2094              break;
2095            }
2096            case NoCompression:
2097            default:
2098            {
2099              /*
2100                Dump uncompressed PseudoColor packets.
2101              */
2102              q=pixels;
2103              for (y=0; y < (ssize_t) image->rows; y++)
2104              {
2105                p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2106                if (p == (const Quantum *) NULL)
2107                  break;
2108                for (x=0; x < (ssize_t) image->columns; x++)
2109                {
2110                  q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
2111                  if ((q-pixels+4) >= 80)
2112                    {
2113                      *q++='\n';
2114                      (void) WriteBlob(image,q-pixels,pixels);
2115                      q=pixels;
2116                    }
2117                  p+=GetPixelChannels(image);
2118                }
2119                if (image->previous == (Image *) NULL)
2120                  {
2121                    status=SetImageProgress(image,SaveImageTag,
2122                      (MagickOffsetType) y,image->rows);
2123                    if (status == MagickFalse)
2124                      break;
2125                  }
2126              }
2127              if (q != pixels)
2128                {
2129                  *q++='\n';
2130                  (void) WriteBlob(image,q-pixels,pixels);
2131                }
2132              break;
2133            }
2134          }
2135          (void) WriteBlobByte(image,'\n');
2136        }
2137    if (LocaleCompare(image_info->magick,"PS") != 0)
2138      (void) WriteBlobString(image,"end\n");
2139    (void) WriteBlobString(image,"%%PageTrailer\n");
2140    if (GetNextImageInList(image) == (Image *) NULL)
2141      break;
2142    image=SyncNextImageInList(image);
2143    status=SetImageProgress(image,SaveImagesTag,scene++,
2144      GetImageListLength(image));
2145    if (status == MagickFalse)
2146      break;
2147  } while (image_info->adjoin != MagickFalse);
2148  (void) WriteBlobString(image,"%%Trailer\n");
2149  if (page > 2)
2150    {
2151      (void) FormatLocaleString(buffer,MaxTextExtent,
2152        "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2153        ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
2154      (void) WriteBlobString(image,buffer);
2155      (void) FormatLocaleString(buffer,MaxTextExtent,
2156        "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
2157        bounds.x2,bounds.y2);
2158      (void) WriteBlobString(image,buffer);
2159    }
2160  (void) WriteBlobString(image,"%%EOF\n");
2161  (void) CloseBlob(image);
2162  return(MagickTrue);
2163}
Note: See TracBrowser for help on using the repository browser.