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