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