root / ImageMagick / trunk / coders / ps.c

Revision 12383, 69.3 kB (checked in by cristy, 3 weeks ago)
Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                               PPPP   SSSSS                                  %
7%                               P   P  SS                                     %
8%                               PPPP    SSS                                   %
9%                               P         SS                                  %
10%                               P      SSSSS                                  %
11%                                                                             %
12%                                                                             %
13%                         Read/Write Postscript Format.                       %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/color.h"
46#include "magick/color-private.h"
47#include "magick/colorspace.h"
48#include "magick/constitute.h"
49#include "magick/delegate.h"
50#include "magick/delegate-private.h"
51#include "magick/draw.h"
52#include "magick/exception.h"
53#include "magick/exception-private.h"
54#include "magick/geometry.h"
55#include "magick/image.h"
56#include "magick/image-private.h"
57#include "magick/list.h"
58#include "magick/magick.h"
59#include "magick/memory_.h"
60#include "magick/monitor.h"
61#include "magick/monitor-private.h"
62#include "magick/option.h"
63#include "magick/profile.h"
64#include "magick/resource_.h"
65#include "magick/pixel-private.h"
66#include "magick/property.h"
67#include "magick/quantum-private.h"
68#include "magick/static.h"
69#include "magick/string_.h"
70#include "magick/module.h"
71#include "magick/token.h"
72#include "magick/transform.h"
73#include "magick/utility.h"
74
75/*
76  Forward declarations.
77*/
78static MagickBooleanType
79  WritePSImage(const ImageInfo *,Image *);
80
81/*
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83%                                                                             %
84%                                                                             %
85%                                                                             %
86%   I n v o k e P o s t s r i p t D e l e g a t e                             %
87%                                                                             %
88%                                                                             %
89%                                                                             %
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91%
92%  InvokePostscriptDelegate() executes the postscript interpreter with the
93%  specified command.
94%
95%  The format of the InvokePostscriptDelegate method is:
96%
97%      MagickBooleanType InvokePostscriptDelegate(
98%        const MagickBooleanType verbose,const char *command)
99%
100%  A description of each parameter follows:
101%
102%    o verbose: A value other than zero displays the command prior to
103%      executing it.
104%
105%    o command: the address of a character string containing the command to
106%      execute.
107%
108*/
109static MagickBooleanType InvokePostscriptDelegate(
110  const MagickBooleanType verbose,const char *command)
111{
112#if defined(MAGICKCORE_GS_DELEGATE) || defined(__WINDOWS__)
113  char
114    **argv;
115
116  const GhostscriptVectors
117    *gs_func;
118
119  gs_main_instance
120    *interpreter;
121
122  int
123    argc,
124    code,
125    status;
126
127  register long
128    i;
129
130#if defined(__WINDOWS__)
131  gs_func=NTGhostscriptDLLVectors();
132#else
133  GhostscriptVectors
134    gs_func_struct;
135
136  gs_func=(&gs_func_struct);
137  (void) ResetMagickMemory(&gs_func,0,sizeof(gs_func));
138  gs_func_struct.new_instance=(int (*)(gs_main_instance **,void *))
139    gsapi_new_instance;
140  gs_func_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
141    gsapi_init_with_args;
142  gs_func_struct.run_string=(int (*)(gs_main_instance *,const char *,int,int *))
143    gsapi_run_string;
144  gs_func_struct.delete_instance=(void (*)(gs_main_instance *))
145    gsapi_delete_instance;
146  gs_func_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
147#endif
148  if (gs_func == (GhostscriptVectors *) NULL)
149    return(SystemCommand(verbose,command) == 0 ? MagickFalse : MagickTrue);
150  if (verbose != MagickFalse)
151    {
152      (void) fputs("[ghostscript library]",stdout);
153      (void) fputs(strchr(command,' '),stdout);
154    }
155  status=(gs_func->new_instance)(&interpreter,(void *) NULL);
156  if (status < 0)
157    return(SystemCommand(verbose,command) == 0 ? MagickFalse : MagickTrue);
158  argv=StringToArgv(command,&argc);
159  status=(gs_func->init_with_args)(interpreter,argc-1,argv+1);
160  if (status == 0)
161    status=(gs_func->run_string)(interpreter,"systemdict /start get exec\n",0,
162      &code);
163  (gs_func->exit)(interpreter);
164  (gs_func->delete_instance)(interpreter);
165#if defined(__WINDOWS__)
166  NTGhostscriptUnLoadDLL();
167#endif
168  for (i=0; i < (long) argc; i++)
169    argv[i]=DestroyString(argv[i]);
170  argv=(char **) RelinquishMagickMemory(argv);
171  if ((status == 0) || (status == -101))
172    return(MagickFalse);
173  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
174    "Ghostscript returns status %d, exit code %d",status,code);
175  return(MagickTrue);
176#else
177  return(SystemCommand(verbose,command) != 0 ? MagickTrue : MagickFalse);
178#endif
179}
180
181/*
182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183%                                                                             %
184%                                                                             %
185%                                                                             %
186%   I s P S                                                                   %
187%                                                                             %
188%                                                                             %
189%                                                                             %
190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191%
192%  IsPS() returns MagickTrue if the image format type, identified by the
193%  magick string, is PS.
194%
195%  The format of the IsPS method is:
196%
197%      MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
198%
199%  A description of each parameter follows:
200%
201%    o magick: This string is generally the first few bytes of an image file
202%      or blob.
203%
204%    o length: Specifies the length of the magick string.
205%
206*/
207static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
208{
209  if (length < 4)
210    return(MagickFalse);
211  if (memcmp(magick,"%!",2) == 0)
212    return(MagickTrue);
213  if (memcmp(magick,"\004%!",3) == 0)
214    return(MagickTrue);
215  return(MagickFalse);
216}
217
218/*
219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220%                                                                             %
221%                                                                             %
222%                                                                             %
223%   R e a d P S I m a g e                                                     %
224%                                                                             %
225%                                                                             %
226%                                                                             %
227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228%
229%  ReadPSImage() reads a Postscript image file and returns it.  It allocates
230%  the memory necessary for the new Image structure and returns a pointer
231%  to the new image.
232%
233%  The format of the ReadPSImage method is:
234%
235%      Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
236%
237%  A description of each parameter follows:
238%
239%    o image_info: the image info.
240%
241%    o exception: return any errors or warnings in this structure.
242%
243*/
244
245static MagickBooleanType IsPostscriptRendered(const char *path)
246{
247  MagickBooleanType
248    status;
249
250  struct stat
251    attributes;
252
253  if ((path == (const char *) NULL) || (*path == '\0'))
254    return(MagickFalse);
255  status=GetPathAttributes(path,&attributes);
256  if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
257      (attributes.st_size > 0))
258    return(MagickTrue);
259  return(MagickFalse);
260}
261
262static inline int ProfileInteger(Image *image,short int *hex_digits)
263{
264  int
265    c,
266    l,
267    value;
268
269  register long
270    i;
271
272  l=0;
273  value=0;
274  for (i=0; i < 2; )
275  {
276    c=ReadBlobByte(image);
277    if ((c == EOF) || ((c == '%') && (l == '%')))
278      {
279        value=(-1);
280        break;
281      }
282    l=c;
283    c&=0xff;
284    if (isxdigit(c) == MagickFalse)
285      continue;
286    value=(int) ((unsigned long) value << 4)+hex_digits[c];
287    i++;
288  }
289  return(value);
290}
291
292static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
293{
294#define BoundingBox  "BoundingBox:"
295#define BeginDocument  "BeginDocument:"
296#define BeginXMLCode  "begin_xml_code"
297#define EndXMLCode  "end_xml_code"
298#define ICCProfile "BeginICCProfile:"
299#define CMYKCustomColor  "CMYKCustomColor:"
300#define DocumentMedia  "DocumentMedia:"
301#define DocumentCustomColors  "DocumentCustomColors:"
302#define DocumentProcessColors  "DocumentProcessColors:"
303#define EndDocument  "EndDocument:"
304#define HiResBoundingBox  "HiResBoundingBox:"
305#define ImageData  "ImageData:"
306#define PageBoundingBox  "PageBoundingBox:"
307#define LanguageLevel  "LanguageLevel:"
308#define PageMedia  "PageMedia:"
309#define Pages  "Pages:"
310#define PhotoshopProfile  "BeginPhotoshop:"
311#define PostscriptLevel  "!PS-"
312#define RenderPostscriptText  "  Rendering Postscript...  "
313#define SpotColor  "+ "
314
315  char
316    command[MaxTextExtent],
317    density[MaxTextExtent],
318    filename[MaxTextExtent],
319    geometry[MaxTextExtent],
320    input_filename[MaxTextExtent],
321    options[MaxTextExtent],
322    postscript_filename[MaxTextExtent],
323    translate_geometry[MaxTextExtent];
324
325  const char
326    *option;
327
328  const DelegateInfo
329    *delegate_info;
330
331  GeometryInfo
332    geometry_info;
333
334  Image
335    *image,
336    *next_image,
337    *postscript_image;
338
339  ImageInfo
340    *read_info;
341
342  int
343    c,
344    file;
345
346  MagickBooleanType
347    cmyk,
348    skip,
349    status;
350
351  MagickStatusType
352    flags;
353
354  PointInfo
355    delta;
356
357  RectangleInfo
358    page;
359
360  register char
361    *p;
362
363  register long
364    i;
365
366  SegmentInfo
367    bounds,
368    hires_bounds;
369
370  short int
371    hex_digits[256];
372
373  size_t
374    length;
375
376  ssize_t
377    count;
378
379  StringInfo
380    *profile;
381
382  unsigned long
383    columns,
384    extent,
385    language_level,
386    pages,
387    rows,
388    scene,
389    spotcolor;
390
391  /*
392    Open image file.
393  */
394  assert(image_info != (const ImageInfo *) NULL);
395  assert(image_info->signature == MagickSignature);
396  if (image_info->debug != MagickFalse)
397    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
398      image_info->filename);
399  assert(exception != (ExceptionInfo *) NULL);
400  assert(exception->signature == MagickSignature);
401  image=AcquireImage(image_info);
402  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
403  if (status == MagickFalse)
404    {
405      image=DestroyImageList(image);
406      return((Image *) NULL);
407    }
408  status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
409  if (status == MagickFalse)
410    {
411      ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
412        image_info->filename);
413      image=DestroyImageList(image);
414      return((Image *) NULL);
415    }
416  /*
417    Initialize hex values.
418  */
419  (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
420  hex_digits[(int) '0']=0;
421  hex_digits[(int) '1']=1;
422  hex_digits[(int) '2']=2;
423  hex_digits[(int) '3']=3;
424  hex_digits[(int) '4']=4;
425  hex_digits[(int) '5']=5;
426  hex_digits[(int) '6']=6;
427  hex_digits[(int) '7']=7;
428  hex_digits[(int) '8']=8;
429  hex_digits[(int) '9']=9;
430  hex_digits[(int) 'a']=10;
431  hex_digits[(int) 'b']=11;
432  hex_digits[(int) 'c']=12;
433  hex_digits[(int) 'd']=13;
434  hex_digits[(int) 'e']=14;
435  hex_digits[(int) 'f']=15;
436  hex_digits[(int) 'A']=10;
437  hex_digits[(int) 'B']=11;
438  hex_digits[(int) 'C']=12;
439  hex_digits[(int) 'D']=13;
440  hex_digits[(int) 'E']=14;
441  hex_digits[(int) 'F']=15;
442  /*
443    Set the page density.
444  */
445  delta.x=DefaultResolution;
446  delta.y=DefaultResolution;
447  if ((image->x_resolution == 0.0) || (image->y_resolution == 0.0))
448    {
449      flags=ParseGeometry(PSDensityGeometry,&geometry_info);
450      image->x_resolution=geometry_info.rho;
451      image->y_resolution=geometry_info.sigma;
452      if ((flags & SigmaValue) == 0)
453        image->y_resolution=image->x_resolution;
454    }
455  /*
456    Determine page geometry from the Postscript bounding box.
457  */
458  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
459  (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
460  (void) ResetMagickMemory(&page,0,sizeof(page));
461  (void) ResetMagickMemory(command,0,sizeof(command));
462  hires_bounds.x2=0.0;
463  hires_bounds.y2=0.0;
464  columns=0;
465  rows=0;
466  extent=0;
467  spotcolor=0;
468  language_level=1;
469  skip=MagickFalse;
470  cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
471  pages=(~0UL);
472  p=command;
473  for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
474  {
475    /*
476      Note document structuring comments.
477    */
478    *p++=(char) c;
479    if ((strchr("\n\r%",c) == (char *) NULL) &&
480        ((size_t) (p-command) < MaxTextExtent))
481      continue;
482    *p='\0';
483    p=command;
484    /*
485      Skip %%BeginDocument thru %%EndDocument.
486    */
487    if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
488      skip=MagickTrue;
489    if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
490      skip=MagickFalse;
491    if (skip != MagickFalse)
492      continue;
493    if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
494      {
495        (void) SetImageProperty(image,"ps:Level",command+4);
496        if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
497          pages=1;
498      }
499    if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
500      (void) sscanf(command,LanguageLevel " %lu",&language_level);
501    if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
502      (void) sscanf(command,Pages " %lu",&pages);
503    if (LocaleNCompare(ImageData,command,strlen(Pages)) == 0)
504      (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
505    if (LocaleNCompare(ICCProfile,command,strlen(PhotoshopProfile)) == 0)
506      {
507        unsigned char
508          *datum;
509
510        /*
511          Read ICC profile.
512        */
513        profile=AcquireStringInfo(65536);
514        for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
515        {
516          SetStringInfoLength(profile,(size_t) i+1);
517          datum=GetStringInfoDatum(profile);
518          datum[i]=(unsigned char) c;
519        }
520        (void) SetImageProfile(image,"icc",profile);
521        profile=DestroyStringInfo(profile);
522        continue;
523      }
524    if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
525      {
526        unsigned char
527          *p;
528
529        /*
530          Read Photoshop profile.
531        */
532        count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
533        if (count != 1)
534          continue;
535        length=extent;
536        profile=AcquireStringInfo(length);
537        p=GetStringInfoDatum(profile);
538        for (i=0; i < (long) length; i++)
539          *p++=(unsigned char) ProfileInteger(image,hex_digits);
540        (void) SetImageProfile(image,"8bim",profile);
541        profile=DestroyStringInfo(profile);
542        continue;
543      }
544    if (LocaleNCompare(BeginXMLCode,command,strlen(BeginXMLCode)) == 0)
545      {
546        /*
547          Read XML code.
548        */
549        c=ReadBlobByte(image);
550        p=command;
551        profile=AcquireStringInfo(0);
552        for (i=0; c != EOF; i++)
553        {
554          SetStringInfoLength(profile,(size_t) i+1);
555          c=ReadBlobByte(image);
556          GetStringInfoDatum(profile)[i]=(unsigned char) c;
557          *p++=(char) c;
558          if ((strchr("\n\r%",c) == (char *) NULL) &&
559              ((size_t) (p-command) < MaxTextExtent))
560            continue;
561          *p='\0';
562          p=command;
563          if (LocaleNCompare(EndXMLCode,command,strlen(EndXMLCode)) == 0)
564            break;
565        }
566        (void) SetImageProfile(image,"xml-code",profile);
567        profile=DestroyStringInfo(profile);
568        continue;
569      }
570    /*
571      Is this a CMYK document?
572    */
573    length=strlen(DocumentProcessColors);
574    if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
575      cmyk=MagickTrue;
576    if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
577      cmyk=MagickTrue;
578    length=strlen(DocumentCustomColors);
579    if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
580        (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
581        (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
582      {
583        char
584          property[MaxTextExtent],
585          *value;
586
587        register char
588          *p;
589
590        /*
591          Note spot names.
592        */
593        (void) FormatMagickString(property,MaxTextExtent,"ps:SpotColor-%lu",
594          spotcolor++);
595        for (p=command; *p != '\0'; p++)
596          if (isspace((int) (unsigned char) *p) != 0)
597            break;
598        value=AcquireString(p);
599        (void) SubstituteString(&value,"(","");
600        (void) SubstituteString(&value,")","");
601        (void) StripString(value);
602        (void) SetImageProperty(image,property,value);
603        value=DestroyString(value);
604        continue;
605      }
606    /*
607      Note region defined by bounding box.
608    */
609    count=0;
610    if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
611      count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",&bounds.x1,
612        &bounds.y1,&bounds.x2,&bounds.y2);
613    if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
614      count=(ssize_t) sscanf(command,DocumentMedia " %*s %lf %lf",&bounds.x2,
615        &bounds.y2)+2;
616    if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
617      count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
618        &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
619    if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
620      count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
621        &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
622    if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
623      count=(ssize_t) sscanf(command,PageMedia " %*s %lf %lf",&bounds.x2,
624        &bounds.y2)+2;
625    if (count != 4)
626      continue;
627    if (((bounds.x2 > hires_bounds.x2) && (bounds.y2 > hires_bounds.y2)) ||
628        ((hires_bounds.x2 == 0.0) && (hires_bounds.y2 == 0.0)))
629      {
630        /*
631          Set Postscript render geometry.
632        */
633        (void) FormatMagickString(geometry,MaxTextExtent,"%gx%g%+g%+g",
634          bounds.x2-bounds.x1,bounds.y2-bounds.y1,bounds.x1,bounds.y1);
635        (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry);
636        page.width=(unsigned long) (bounds.x2-bounds.x1+0.5);
637        page.height=(unsigned long) (bounds.y2-bounds.y1+0.5);
638        hires_bounds=bounds;
639      }
640  }
641  (void) CloseBlob(image);
642  if (image_info->colorspace == RGBColorspace)
643    cmyk=MagickFalse;
644  /*
645    Create Ghostscript control file.
646  */
647  file=AcquireUniqueFileResource(postscript_filename);
648  if (file == -1)
649    {
650      ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
651        image_info->filename);
652      image=DestroyImageList(image);
653      return((Image *) NULL);
654    }
655  (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
656    "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
657    "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
658  (void) write(file,command,(unsigned int) strlen(command));
659  (void) FormatMagickString(translate_geometry,MaxTextExtent,
660    "%g %g translate\n",-bounds.x1,-bounds.y1);
661  (void) write(file,translate_geometry,strlen(translate_geometry));
662  file=close(file)-1;
663  /*
664    Render Postscript with the Ghostscript delegate.
665  */
666  if (image_info->monochrome != MagickFalse)
667    delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
668  else
669    if (cmyk != MagickFalse)
670      delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
671    else
672      if (pages == 1)
673        delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
674      else
675        delegate_info=GetDelegateInfo("ps:color",(char *) NULL,exception);
676  if (delegate_info == (const DelegateInfo *) NULL)
677    {
678      (void) RelinquishUniqueFileResource(postscript_filename);
679      image=DestroyImageList(image);
680      return((Image *) NULL);
681    }
682  *options='\0';
683  if ((page.width == 0) || (page.height == 0))
684    (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
685  if (image_info->page != (char *) NULL)
686    (void) ParseAbsoluteGeometry(image_info->page,&page);
687  (void) FormatMagickString(density,MaxTextExtent,"%gx%g",image->x_resolution,
688    image->y_resolution);
689  page.width=(unsigned long) (page.width*image->x_resolution/delta.x+0.5);
690  page.height=(unsigned long) (page.height*image->y_resolution/delta.y+0.5);
691  (void) FormatMagickString(options,MaxTextExtent,"-g%lux%lu ",
692    page.width,page.height);
693  read_info=CloneImageInfo(image_info);
694  *read_info->magick='\0';
695  if (read_info->number_scenes != 0)
696    {
697      char
698        pages[MaxTextExtent];
699
700      (void) FormatMagickString(pages,MaxTextExtent,"-dFirstPage=%lu "
701        "-dLastPage=%lu",read_info->scene+1,read_info->scene+
702        read_info->number_scenes);
703      (void) ConcatenateMagickString(options,pages,MaxTextExtent);
704      read_info->number_scenes=0;
705      if (read_info->scenes != (char *) NULL)
706        *read_info->scenes='\0';
707    }
708  option=GetImageOption(image_info,"ps:use-cropbox");
709  if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
710    (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
711  (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
712  (void) AcquireUniqueFilename(read_info->filename);
713  (void) FormatMagickString(command,MaxTextExtent,
714    GetDelegateCommands(delegate_info),
715    read_info->antialias != MagickFalse ? 4 : 1,
716    read_info->antialias != MagickFalse ? 4 : 1,density,options,
717    read_info->filename,postscript_filename,input_filename);
718  status=InvokePostscriptDelegate(read_info->verbose,command);
719  if ((status != MagickFalse) ||
720      (IsPostscriptRendered(read_info->filename) == MagickFalse))
721    {
722      (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
723      status=InvokePostscriptDelegate(read_info->verbose,command);
724    }
725  postscript_image=(Image *) NULL;
726  if (status == MagickFalse)
727    postscript_image=ReadImage(read_info,exception);
728  (void) RelinquishUniqueFileResource(postscript_filename);
729  (void) RelinquishUniqueFileResource(read_info->filename);
730  (void) RelinquishUniqueFileResource(input_filename);
731  read_info=DestroyImageInfo(read_info);
732  if (postscript_image == (Image *) NULL)
733    {
734      image=DestroyImageList(image);
735      ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
736        image_info->filename);
737      return((Image *) NULL);
738    }
739  if (LocaleCompare(postscript_image->magick,"BMP") == 0)
740    {
741      Image
742        *cmyk_image;
743
744      cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
745      if (cmyk_image != (Image *) NULL)
746        {
747          postscript_image=DestroyImageList(postscript_image);
748          postscript_image=cmyk_image;
749        }
750    }
751  if (image_info->number_scenes != 0)
752    {
753      Image
754        *clone_image;
755
756      register long
757        i;
758
759      /*
760        Add place holder images to meet the subimage specification requirement.
761      */
762      for (i=0; i < (long) image_info->scene; i++)
763      {
764        clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
765        if (clone_image != (Image *) NULL)
766          PrependImageToList(&postscript_image,clone_image);
767      }
768    }
769  scene=0;
770  do
771  {
772    (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
773    if (columns != 0)
774      postscript_image->magick_columns=columns;
775    if (rows != 0)
776      postscript_image->magick_rows=rows;
777    postscript_image->page=page;
778    postscript_image->scene=scene++;
779    (void) CloneImageProfiles(postscript_image,image);
780    (void) CloneImageProperties(postscript_image,image);
781    next_image=SyncNextImageInList(postscript_image);
782    if (next_image != (Image *) NULL)
783      postscript_image=next_image;
784  } while (next_image != (Image *) NULL);
785  image=DestroyImageList(image);
786  return(GetFirstImageInList(postscript_image));
787}
788
789/*
790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791%                                                                             %
792%                                                                             %
793%                                                                             %
794%   R e g i s t e r P S I m a g e                                             %
795%                                                                             %
796%                                                                             %
797%                                                                             %
798%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799%
800%  RegisterPSImage() adds properties for the PS image format to
801%  the list of supported formats.  The properties include the image format
802%  tag, a method to read and/or write the format, whether the format
803%  supports the saving of more than one frame to the same file or blob,
804%  whether the format supports native in-memory I/O, and a brief
805%  description of the format.
806%
807%  The format of the RegisterPSImage method is:
808%
809%      unsigned long RegisterPSImage(void)
810%
811*/
812ModuleExport unsigned long RegisterPSImage(void)
813{
814  MagickInfo
815    *entry;
816
817  entry=SetMagickInfo("EPI");
818  entry->decoder=(DecodeImageHandler *) ReadPSImage;
819  entry->encoder=(EncodeImageHandler *) WritePSImage;
820  entry->magick=(IsImageFormatHandler *) IsPS;
821  entry->adjoin=MagickFalse;
822  entry->blob_support=MagickFalse;
823  entry->seekable_stream=MagickTrue;
824  entry->thread_support=EncoderThreadSupport;
825  entry->description=ConstantString(
826   "Encapsulated PostScript Interchange format");
827  entry->module=ConstantString("PS");
828  (void) RegisterMagickInfo(entry);
829  entry=SetMagickInfo("EPS");
830  entry->decoder=(DecodeImageHandler *) ReadPSImage;
831  entry->encoder=(EncodeImageHandler *) WritePSImage;
832  entry->magick=(IsImageFormatHandler *) IsPS;
833  entry->adjoin=MagickFalse;
834  entry->blob_support=MagickFalse;
835  entry->seekable_stream=MagickTrue;
836  entry->thread_support=EncoderThreadSupport;
837  entry->description=ConstantString("Encapsulated PostScript");
838  entry->module=ConstantString("PS");
839  (void) RegisterMagickInfo(entry);
840  entry=SetMagickInfo("EPSF");
841  entry->decoder=(DecodeImageHandler *) ReadPSImage;
842  entry->encoder=(EncodeImageHandler *) WritePSImage;
843  entry->magick=(IsImageFormatHandler *) IsPS;
844  entry->adjoin=MagickFalse;
845  entry->blob_support=MagickFalse;
846  entry->seekable_stream=MagickTrue;
847  entry->description=ConstantString("Encapsulated PostScript");
848  entry->module=ConstantString("PS");
849  (void) RegisterMagickInfo(entry);
850  entry=SetMagickInfo("EPSI");
851  entry->decoder=(DecodeImageHandler *) ReadPSImage;
852  entry->encoder=(EncodeImageHandler *) WritePSImage;
853  entry->magick=(IsImageFormatHandler *) IsPS;
854  entry->adjoin=MagickFalse;
855  entry->blob_support=MagickFalse;
856  entry->seekable_stream=MagickTrue;
857  entry->thread_support=EncoderThreadSupport;
858  entry->description=ConstantString(
859    "Encapsulated PostScript Interchange format");
860  entry->module=ConstantString("PS");
861  (void) RegisterMagickInfo(entry);
862  entry=SetMagickInfo("PS");
863  entry->decoder=(DecodeImageHandler *) ReadPSImage;
864  entry->encoder=(EncodeImageHandler *) WritePSImage;
865  entry->magick=(IsImageFormatHandler *) IsPS;
866  entry->module=ConstantString("PS");
867  entry->blob_support=MagickFalse;
868  entry->seekable_stream=MagickTrue;
869  entry->thread_support=EncoderThreadSupport;
870  entry->description=ConstantString("PostScript");
871  (void) RegisterMagickInfo(entry);
872  return(MagickImageCoderSignature);
873}
874
875/*
876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
877%                                                                             %
878%                                                                             %
879%                                                                             %
880%   U n r e g i s t e r P S I m a g e                                         %
881%                                                                             %
882%                                                                             %
883%                                                                             %
884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
885%
886%  UnregisterPSImage() removes format registrations made by the
887%  PS module from the list of supported formats.
888%
889%  The format of the UnregisterPSImage method is:
890%
891%      UnregisterPSImage(void)
892%
893*/
894ModuleExport void UnregisterPSImage(void)
895{
896  (void) UnregisterMagickInfo("EPI");
897  (void) UnregisterMagickInfo("EPS");
898  (void) UnregisterMagickInfo("EPSF");
899  (void) UnregisterMagickInfo("EPSI");
900  (void) UnregisterMagickInfo("PS");
901}
902
903/*
904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
905%                                                                             %
906%                                                                             %
907%                                                                             %
908%   W r i t e P S I m a g e                                                   %
909%                                                                             %
910%                                                                             %
911%                                                                             %
912%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
913%
914%  WritePSImage translates an image to encapsulated Postscript
915%  Level I for printing.  If the supplied geometry is null, the image is
916%  centered on the Postscript page.  Otherwise, the image is positioned as
917%  specified by the geometry.
918%
919%  The format of the WritePSImage method is:
920%
921%      MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
922%
923%  A description of each parameter follows:
924%
925%    o image_info: the image info.
926%
927%    o image: the image.
928%
929*/
930
931static inline size_t MagickMin(const size_t x,const size_t y)
932{
933  if (x < y)
934    return(x);
935  return(y);
936}
937
938static inline unsigned char *PopHexPixel(const char **hex_digits,
939  const unsigned long pixel,unsigned char *pixels)
940{
941  register const char
942    *hex;
943
944  hex=hex_digits[pixel];
945  *pixels++=(unsigned char) (*hex++);
946  *pixels++=(unsigned char) (*hex);
947  return(pixels);
948}
949
950static MagickBooleanType Write