2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Postscript Format %
20 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/colorspace-private.h"
51 #include "MagickCore/constitute.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/delegate-private.h"
54 #include "MagickCore/draw.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/geometry.h"
58 #include "MagickCore/image.h"
59 #include "MagickCore/image-private.h"
60 #include "MagickCore/list.h"
61 #include "MagickCore/magick.h"
62 #include "MagickCore/memory_.h"
63 #include "MagickCore/monitor.h"
64 #include "MagickCore/monitor-private.h"
65 #include "MagickCore/option.h"
66 #include "MagickCore/profile.h"
67 #include "MagickCore/resource_.h"
68 #include "MagickCore/pixel-accessor.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/quantum-private.h"
71 #include "MagickCore/static.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/module.h"
74 #include "MagickCore/token.h"
75 #include "MagickCore/transform.h"
76 #include "MagickCore/utility.h"
81 static MagickBooleanType
82 WritePSImage(const ImageInfo *,Image *,ExceptionInfo *);
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89 % I n v o k e P o s t s r i p t D e l e g a t e %
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 % InvokePostscriptDelegate() executes the Postscript interpreter with the
98 % The format of the InvokePostscriptDelegate method is:
100 % MagickBooleanType InvokePostscriptDelegate(
101 % const MagickBooleanType verbose,const char *command,
102 % ExceptionInfo *exception)
104 % A description of each parameter follows:
106 % o verbose: A value other than zero displays the command prior to
109 % o command: the address of a character string containing the command to
112 % o exception: return any errors or warnings in this structure.
115 static MagickBooleanType InvokePostscriptDelegate(
116 const MagickBooleanType verbose,const char *command,ExceptionInfo *exception)
121 #if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
138 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
139 ghost_info=NTGhostscriptDLLVectors();
144 ghost_info=(&ghost_info_struct);
145 (void) ResetMagickMemory(&ghost_info,0,sizeof(ghost_info));
146 ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
148 ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
149 gsapi_init_with_args;
150 ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
151 int *)) gsapi_run_string;
152 ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
153 gsapi_delete_instance;
154 ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
156 if (ghost_info == (GhostInfo *) NULL)
158 status=SystemCommand(MagickFalse,verbose,command,exception);
159 return(status == 0 ? MagickTrue : MagickFalse);
161 if (verbose != MagickFalse)
163 (void) fputs("[ghostscript library]",stdout);
164 (void) fputs(strchr(command,' '),stdout);
166 status=(ghost_info->new_instance)(&interpreter,(void *) NULL);
169 status=SystemCommand(MagickFalse,verbose,command,exception);
170 return(status == 0 ? MagickTrue : MagickFalse);
173 argv=StringToArgv(command,&argc);
174 status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
176 status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
178 (ghost_info->exit)(interpreter);
179 (ghost_info->delete_instance)(interpreter);
180 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
181 NTGhostscriptUnLoadDLL();
183 for (i=0; i < (ssize_t) argc; i++)
184 argv[i]=DestroyString(argv[i]);
185 argv=(char **) RelinquishMagickMemory(argv);
186 if ((status != 0) && (status != -101))
191 message=GetExceptionMessage(errno);
192 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
193 "`%s': %s",command,message);
194 message=DestroyString(message);
195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
196 "Ghostscript returns status %d, exit code %d",status,code);
201 status=SystemCommand(MagickFalse,verbose,command,exception);
202 return(status == 0 ? MagickTrue : MagickFalse);
207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
217 % IsPS() returns MagickTrue if the image format type, identified by the
218 % magick string, is PS.
220 % The format of the IsPS method is:
222 % MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
224 % A description of each parameter follows:
226 % o magick: compare image format pattern against these bytes.
228 % o length: Specifies the length of the magick string.
231 static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
235 if (memcmp(magick,"%!",2) == 0)
237 if (memcmp(magick,"\004%!",3) == 0)
243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 % R e a d P S I m a g e %
251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253 % ReadPSImage() reads a Postscript image file and returns it. It allocates
254 % the memory necessary for the new Image structure and returns a pointer
257 % The format of the ReadPSImage method is:
259 % Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
261 % A description of each parameter follows:
263 % o image_info: the image info.
265 % o exception: return any errors or warnings in this structure.
269 static MagickBooleanType IsPostscriptRendered(const char *path)
277 if ((path == (const char *) NULL) || (*path == '\0'))
279 status=GetPathAttributes(path,&attributes);
280 if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
281 (attributes.st_size > 0))
286 static inline int ProfileInteger(Image *image,short int *hex_digits)
300 c=ReadBlobByte(image);
301 if ((c == EOF) || ((c == '%') && (l == '%')))
308 if (isxdigit(c) == MagickFalse)
310 value=(int) ((size_t) value << 4)+hex_digits[c];
316 static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
318 #define BoundingBox "BoundingBox:"
319 #define BeginDocument "BeginDocument:"
320 #define BeginXMPPacket "<?xpacket begin="
321 #define EndXMPPacket "<?xpacket end="
322 #define ICCProfile "BeginICCProfile:"
323 #define CMYKCustomColor "CMYKCustomColor:"
324 #define CMYKProcessColor "CMYKProcessColor:"
325 #define DocumentMedia "DocumentMedia:"
326 #define DocumentCustomColors "DocumentCustomColors:"
327 #define DocumentProcessColors "DocumentProcessColors:"
328 #define EndDocument "EndDocument:"
329 #define HiResBoundingBox "HiResBoundingBox:"
330 #define ImageData "ImageData:"
331 #define PageBoundingBox "PageBoundingBox:"
332 #define LanguageLevel "LanguageLevel:"
333 #define PageMedia "PageMedia:"
334 #define Pages "Pages:"
335 #define PhotoshopProfile "BeginPhotoshop:"
336 #define PostscriptLevel "!PS-"
337 #define RenderPostscriptText " Rendering Postscript... "
338 #define SpotColor "+ "
341 command[MaxTextExtent],
342 density[MaxTextExtent],
343 filename[MaxTextExtent],
344 geometry[MaxTextExtent],
345 input_filename[MaxTextExtent],
346 options[MaxTextExtent],
347 postscript_filename[MaxTextExtent];
419 assert(image_info != (const ImageInfo *) NULL);
420 assert(image_info->signature == MagickSignature);
421 if (image_info->debug != MagickFalse)
422 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
423 image_info->filename);
424 assert(exception != (ExceptionInfo *) NULL);
425 assert(exception->signature == MagickSignature);
426 image=AcquireImage(image_info,exception);
427 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
428 if (status == MagickFalse)
430 image=DestroyImageList(image);
431 return((Image *) NULL);
433 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
434 if (status == MagickFalse)
436 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
437 image_info->filename);
438 image=DestroyImageList(image);
439 return((Image *) NULL);
442 Initialize hex values.
444 (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
445 hex_digits[(int) '0']=0;
446 hex_digits[(int) '1']=1;
447 hex_digits[(int) '2']=2;
448 hex_digits[(int) '3']=3;
449 hex_digits[(int) '4']=4;
450 hex_digits[(int) '5']=5;
451 hex_digits[(int) '6']=6;
452 hex_digits[(int) '7']=7;
453 hex_digits[(int) '8']=8;
454 hex_digits[(int) '9']=9;
455 hex_digits[(int) 'a']=10;
456 hex_digits[(int) 'b']=11;
457 hex_digits[(int) 'c']=12;
458 hex_digits[(int) 'd']=13;
459 hex_digits[(int) 'e']=14;
460 hex_digits[(int) 'f']=15;
461 hex_digits[(int) 'A']=10;
462 hex_digits[(int) 'B']=11;
463 hex_digits[(int) 'C']=12;
464 hex_digits[(int) 'D']=13;
465 hex_digits[(int) 'E']=14;
466 hex_digits[(int) 'F']=15;
468 Set the page density.
470 delta.x=DefaultResolution;
471 delta.y=DefaultResolution;
472 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
474 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
475 image->resolution.x=geometry_info.rho;
476 image->resolution.y=geometry_info.sigma;
477 if ((flags & SigmaValue) == 0)
478 image->resolution.y=image->resolution.x;
480 if (image_info->density != (char *) NULL)
482 flags=ParseGeometry(image_info->density,&geometry_info);
483 image->resolution.x=geometry_info.rho;
484 image->resolution.y=geometry_info.sigma;
485 if ((flags & SigmaValue) == 0)
486 image->resolution.y=image->resolution.x;
488 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
489 if (image_info->page != (char *) NULL)
490 (void) ParseAbsoluteGeometry(image_info->page,&page);
491 page.width=(size_t) ceil((double) (page.width*image->resolution.x/delta.x)-
493 page.height=(size_t) ceil((double) (page.height*image->resolution.y/delta.y)-
496 Determine page geometry from the Postscript bounding box.
498 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
499 (void) ResetMagickMemory(command,0,sizeof(command));
500 cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
501 (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
510 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
513 Note document structuring comments.
516 if ((strchr("\n\r%",c) == (char *) NULL) &&
517 ((size_t) (p-command) < (MaxTextExtent-1)))
522 Skip %%BeginDocument thru %%EndDocument.
524 if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
526 if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
528 if (skip != MagickFalse)
530 if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
532 (void) SetImageProperty(image,"ps:Level",command+4,exception);
533 if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
536 if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
537 (void) sscanf(command,LanguageLevel " %lu",&language_level);
538 if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
539 (void) sscanf(command,Pages " %lu",&pages);
540 if (LocaleNCompare(ImageData,command,strlen(ImageData)) == 0)
541 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
542 if (LocaleNCompare(ICCProfile,command,strlen(ICCProfile)) == 0)
550 profile=AcquireStringInfo(65536);
551 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
553 SetStringInfoLength(profile,(size_t) i+1);
554 datum=GetStringInfoDatum(profile);
555 datum[i]=(unsigned char) c;
557 (void) SetImageProfile(image,"icc",profile,exception);
558 profile=DestroyStringInfo(profile);
561 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
567 Read Photoshop profile.
569 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
573 profile=BlobToStringInfo((const void *) NULL,length);
574 p=GetStringInfoDatum(profile);
575 for (i=0; i < (ssize_t) length; i++)
576 *p++=(unsigned char) ProfileInteger(image,hex_digits);
577 (void) SetImageProfile(image,"8bim",profile,exception);
578 profile=DestroyStringInfo(profile);
581 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
590 profile=StringToStringInfo(command);
591 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
593 SetStringInfoLength(profile,i+1);
594 c=ReadBlobByte(image);
595 GetStringInfoDatum(profile)[i]=(unsigned char) c;
597 if ((strchr("\n\r%",c) == (char *) NULL) &&
598 ((size_t) (p-command) < (MaxTextExtent-1)))
602 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
605 SetStringInfoLength(profile,i);
606 (void) SetImageProfile(image,"xmp",profile,exception);
607 profile=DestroyStringInfo(profile);
611 Is this a CMYK document?
613 length=strlen(DocumentProcessColors);
614 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
616 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
617 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
618 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
621 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
623 if (LocaleNCompare(CMYKProcessColor,command,strlen(CMYKProcessColor)) == 0)
625 length=strlen(DocumentCustomColors);
626 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
627 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
628 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
631 property[MaxTextExtent],
640 (void) FormatLocaleString(property,MaxTextExtent,"ps:SpotColor-%.20g",
641 (double) (spotcolor++));
642 for (p=command; *p != '\0'; p++)
643 if (isspace((int) (unsigned char) *p) != 0)
645 value=AcquireString(p);
646 (void) SubstituteString(&value,"(","");
647 (void) SubstituteString(&value,")","");
648 (void) StripString(value);
649 (void) SetImageProperty(image,property,value,exception);
650 value=DestroyString(value);
653 if (image_info->page != (char *) NULL)
656 Note region defined by bounding box.
660 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
662 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",
663 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
666 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
668 count=(ssize_t) sscanf(command,DocumentMedia " %lf %lf %lf %lf",
669 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
672 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
674 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
675 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
678 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
680 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
681 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
684 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
686 count=(ssize_t) sscanf(command,PageMedia " %lf %lf %lf %lf",
687 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
690 if ((count != 4) || (i < (ssize_t) priority))
695 if ((fabs(hires_bounds.x2-hires_bounds.x1) >= MagickEpsilon) &&
696 (fabs(hires_bounds.y2-hires_bounds.y1) >= MagickEpsilon))
699 Set Postscript render geometry.
701 (void) FormatLocaleString(geometry,MaxTextExtent,"%gx%g%+.15g%+.15g",
702 hires_bounds.x2-hires_bounds.x1,hires_bounds.y2-hires_bounds.y1,
703 hires_bounds.x1,hires_bounds.y1);
704 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry,exception);
705 page.width=(size_t) ceil((double) ((hires_bounds.x2-hires_bounds.x1)*
706 image->resolution.x/delta.x)-0.5);
707 page.height=(size_t) ceil((double) ((hires_bounds.y2-hires_bounds.y1)*
708 image->resolution.y/delta.y)-0.5);
710 (void) CloseBlob(image);
711 if (IsRGBColorspace(image_info->colorspace) != MagickFalse)
714 Create Ghostscript control file.
716 file=AcquireUniqueFileResource(postscript_filename);
719 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
720 image_info->filename);
721 image=DestroyImageList(image);
722 return((Image *) NULL);
724 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
725 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
726 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
727 count=write(file,command,(unsigned int) strlen(command));
728 if (image_info->page == (char *) NULL)
731 translate_geometry[MaxTextExtent];
733 (void) FormatLocaleString(translate_geometry,MaxTextExtent,
734 "%g %g translate\n",-bounds.x1,-bounds.y1);
735 count=write(file,translate_geometry,(unsigned int)
736 strlen(translate_geometry));
740 Render Postscript with the Ghostscript delegate.
742 if (image_info->monochrome != MagickFalse)
743 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
745 if (cmyk != MagickFalse)
746 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
748 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
749 if (delegate_info == (const DelegateInfo *) NULL)
751 (void) RelinquishUniqueFileResource(postscript_filename);
752 image=DestroyImageList(image);
753 return((Image *) NULL);
756 (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",
757 image->resolution.x,image->resolution.y);
758 (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
759 page.width,(double) page.height);
760 read_info=CloneImageInfo(image_info);
761 *read_info->magick='\0';
762 if (read_info->number_scenes != 0)
765 pages[MaxTextExtent];
767 (void) FormatLocaleString(pages,MaxTextExtent,"-dFirstPage=%.20g "
768 "-dLastPage=%.20g",(double) read_info->scene+1,(double)
769 (read_info->scene+read_info->number_scenes));
770 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
771 read_info->number_scenes=0;
772 if (read_info->scenes != (char *) NULL)
773 *read_info->scenes='\0';
775 option=GetImageOption(image_info,"ps:use-cropbox");
776 if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
777 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
778 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
779 (void) AcquireUniqueFilename(filename);
780 (void) ConcatenateMagickString(filename,"-%08d",MaxTextExtent);
781 (void) FormatLocaleString(command,MaxTextExtent,
782 GetDelegateCommands(delegate_info),
783 read_info->antialias != MagickFalse ? 4 : 1,
784 read_info->antialias != MagickFalse ? 4 : 1,density,options,filename,
785 postscript_filename,input_filename);
786 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
787 (void) InterpretImageFilename(image_info,image,filename,1,
788 read_info->filename,exception);
789 if ((status == MagickFalse) ||
790 (IsPostscriptRendered(read_info->filename) == MagickFalse))
792 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
793 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
795 (void) RelinquishUniqueFileResource(postscript_filename);
796 (void) RelinquishUniqueFileResource(input_filename);
797 postscript_image=(Image *) NULL;
798 if (status == MagickFalse)
801 (void) InterpretImageFilename(image_info,image,filename,(int) i,
802 read_info->filename,exception);
803 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
805 (void) RelinquishUniqueFileResource(read_info->filename);
810 (void) InterpretImageFilename(image_info,image,filename,(int) i,
811 read_info->filename,exception);
812 if (IsPostscriptRendered(read_info->filename) == MagickFalse)
814 next=ReadImage(read_info,exception);
815 (void) RelinquishUniqueFileResource(read_info->filename);
816 if (next == (Image *) NULL)
818 AppendImageToList(&postscript_image,next);
820 (void) RelinquishUniqueFileResource(read_info->filename);
821 read_info=DestroyImageInfo(read_info);
822 if (postscript_image == (Image *) NULL)
824 image=DestroyImageList(image);
825 ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
826 image_info->filename);
827 return((Image *) NULL);
829 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
834 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
835 if (cmyk_image != (Image *) NULL)
837 postscript_image=DestroyImageList(postscript_image);
838 postscript_image=cmyk_image;
841 if (image_info->number_scenes != 0)
850 Add place holder images to meet the subimage specification requirement.
852 for (i=0; i < (ssize_t) image_info->scene; i++)
854 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
855 if (clone_image != (Image *) NULL)
856 PrependImageToList(&postscript_image,clone_image);
861 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
863 postscript_image->magick_columns=columns;
865 postscript_image->magick_rows=rows;
866 postscript_image->page=page;
867 (void) CloneImageProfiles(postscript_image,image);
868 (void) CloneImageProperties(postscript_image,image);
869 next=SyncNextImageInList(postscript_image);
870 if (next != (Image *) NULL)
871 postscript_image=next;
872 } while (next != (Image *) NULL);
873 image=DestroyImageList(image);
875 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
878 next=GetNextImageInList(next);
880 return(GetFirstImageInList(postscript_image));
884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
888 % R e g i s t e r P S I m a g e %
892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
894 % RegisterPSImage() adds properties for the PS image format to
895 % the list of supported formats. The properties include the image format
896 % tag, a method to read and/or write the format, whether the format
897 % supports the saving of more than one frame to the same file or blob,
898 % whether the format supports native in-memory I/O, and a brief
899 % description of the format.
901 % The format of the RegisterPSImage method is:
903 % size_t RegisterPSImage(void)
906 ModuleExport size_t RegisterPSImage(void)
911 entry=SetMagickInfo("EPI");
912 entry->decoder=(DecodeImageHandler *) ReadPSImage;
913 entry->encoder=(EncodeImageHandler *) WritePSImage;
914 entry->magick=(IsImageFormatHandler *) IsPS;
915 entry->adjoin=MagickFalse;
916 entry->blob_support=MagickFalse;
917 entry->seekable_stream=MagickTrue;
918 entry->thread_support=EncoderThreadSupport;
919 entry->description=ConstantString(
920 "Encapsulated PostScript Interchange format");
921 entry->module=ConstantString("PS");
922 (void) RegisterMagickInfo(entry);
923 entry=SetMagickInfo("EPS");
924 entry->decoder=(DecodeImageHandler *) ReadPSImage;
925 entry->encoder=(EncodeImageHandler *) WritePSImage;
926 entry->magick=(IsImageFormatHandler *) IsPS;
927 entry->adjoin=MagickFalse;
928 entry->blob_support=MagickFalse;
929 entry->seekable_stream=MagickTrue;
930 entry->thread_support=EncoderThreadSupport;
931 entry->description=ConstantString("Encapsulated PostScript");
932 entry->module=ConstantString("PS");
933 (void) RegisterMagickInfo(entry);
934 entry=SetMagickInfo("EPSF");
935 entry->decoder=(DecodeImageHandler *) ReadPSImage;
936 entry->encoder=(EncodeImageHandler *) WritePSImage;
937 entry->magick=(IsImageFormatHandler *) IsPS;
938 entry->adjoin=MagickFalse;
939 entry->blob_support=MagickFalse;
940 entry->seekable_stream=MagickTrue;
941 entry->description=ConstantString("Encapsulated PostScript");
942 entry->module=ConstantString("PS");
943 (void) RegisterMagickInfo(entry);
944 entry=SetMagickInfo("EPSI");
945 entry->decoder=(DecodeImageHandler *) ReadPSImage;
946 entry->encoder=(EncodeImageHandler *) WritePSImage;
947 entry->magick=(IsImageFormatHandler *) IsPS;
948 entry->adjoin=MagickFalse;
949 entry->blob_support=MagickFalse;
950 entry->seekable_stream=MagickTrue;
951 entry->thread_support=EncoderThreadSupport;
952 entry->description=ConstantString(
953 "Encapsulated PostScript Interchange format");
954 entry->module=ConstantString("PS");
955 (void) RegisterMagickInfo(entry);
956 entry=SetMagickInfo("PS");
957 entry->decoder=(DecodeImageHandler *) ReadPSImage;
958 entry->encoder=(EncodeImageHandler *) WritePSImage;
959 entry->magick=(IsImageFormatHandler *) IsPS;
960 entry->module=ConstantString("PS");
961 entry->blob_support=MagickFalse;
962 entry->seekable_stream=MagickTrue;
963 entry->thread_support=EncoderThreadSupport;
964 entry->description=ConstantString("PostScript");
965 (void) RegisterMagickInfo(entry);
966 return(MagickImageCoderSignature);
970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
974 % U n r e g i s t e r P S I m a g e %
978 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
980 % UnregisterPSImage() removes format registrations made by the
981 % PS module from the list of supported formats.
983 % The format of the UnregisterPSImage method is:
985 % UnregisterPSImage(void)
988 ModuleExport void UnregisterPSImage(void)
990 (void) UnregisterMagickInfo("EPI");
991 (void) UnregisterMagickInfo("EPS");
992 (void) UnregisterMagickInfo("EPSF");
993 (void) UnregisterMagickInfo("EPSI");
994 (void) UnregisterMagickInfo("PS");
998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002 % W r i t e P S I m a g e %
1006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1008 % WritePSImage translates an image to encapsulated Postscript
1009 % Level I for printing. If the supplied geometry is null, the image is
1010 % centered on the Postscript page. Otherwise, the image is positioned as
1011 % specified by the geometry.
1013 % The format of the WritePSImage method is:
1015 % MagickBooleanType WritePSImage(const ImageInfo *image_info,
1016 % Image *image,ExceptionInfo *exception)
1018 % A description of each parameter follows:
1020 % o image_info: the image info.
1022 % o image: the image.
1024 % o exception: return any errors or warnings in this structure.
1028 static inline size_t MagickMin(const size_t x,const size_t y)
1035 static inline unsigned char *PopHexPixel(const char **hex_digits,
1036 const size_t pixel,unsigned char *pixels)
1041 hex=hex_digits[pixel];
1042 *pixels++=(unsigned char) (*hex++);
1043 *pixels++=(unsigned char) (*hex);
1047 static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image,
1048 ExceptionInfo *exception)
1050 #define WriteRunlengthPacket(image,pixel,length,p) \
1052 if ((image->matte != MagickFalse) && \
1053 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha)) \
1055 q=PopHexPixel(hex_digits,0xff,q); \
1056 q=PopHexPixel(hex_digits,0xff,q); \
1057 q=PopHexPixel(hex_digits,0xff,q); \
1061 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
1062 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
1063 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1065 q=PopHexPixel(hex_digits,(size_t) MagickMin(length,0xff),q); \
1071 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1072 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1073 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1074 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1075 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1076 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1077 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1078 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1079 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1080 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1081 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1082 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1083 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1084 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1085 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1086 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1087 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1088 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1089 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1090 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1091 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1092 "FC", "FD", "FE", "FF", (char *) NULL
1094 *PostscriptProlog[]=
1098 "% Display a color image. The image is displayed in color on",
1099 "% Postscript viewers or printers that support color, otherwise",
1100 "% it is displayed as grayscale.",
1102 "/DirectClassPacket",
1105 " % Get a DirectClass packet.",
1111 " % length: number of pixels minus one of this color (optional).",
1113 " currentfile color_packet readhexstring pop pop",
1114 " compression 0 eq",
1116 " /number_pixels 3 def",
1119 " currentfile byte readhexstring pop 0 get",
1120 " /number_pixels exch 1 add 3 mul def",
1122 " 0 3 number_pixels 1 sub",
1124 " pixels exch color_packet putinterval",
1126 " pixels 0 number_pixels getinterval",
1129 "/DirectClassImage",
1132 " % Display a DirectClass image.",
1134 " systemdict /colorimage known",
1141 " { DirectClassPacket } false 3 colorimage",
1145 " % No colorimage operator; convert to grayscale.",
1152 " { GrayDirectClassPacket } image",
1156 "/GrayDirectClassPacket",
1159 " % Get a DirectClass packet; convert to grayscale.",
1165 " % length: number of pixels minus one of this color (optional).",
1167 " currentfile color_packet readhexstring pop pop",
1168 " color_packet 0 get 0.299 mul",
1169 " color_packet 1 get 0.587 mul add",
1170 " color_packet 2 get 0.114 mul add",
1172 " /gray_packet exch def",
1173 " compression 0 eq",
1175 " /number_pixels 1 def",
1178 " currentfile byte readhexstring pop 0 get",
1179 " /number_pixels exch 1 add def",
1181 " 0 1 number_pixels 1 sub",
1183 " pixels exch gray_packet put",
1185 " pixels 0 number_pixels getinterval",
1188 "/GrayPseudoClassPacket",
1191 " % Get a PseudoClass packet; convert to grayscale.",
1194 " % index: index into the colormap.",
1195 " % length: number of pixels minus one of this color (optional).",
1197 " currentfile byte readhexstring pop 0 get",
1198 " /offset exch 3 mul def",
1199 " /color_packet colormap offset 3 getinterval def",
1200 " color_packet 0 get 0.299 mul",
1201 " color_packet 1 get 0.587 mul add",
1202 " color_packet 2 get 0.114 mul add",
1204 " /gray_packet exch def",
1205 " compression 0 eq",
1207 " /number_pixels 1 def",
1210 " currentfile byte readhexstring pop 0 get",
1211 " /number_pixels exch 1 add def",
1213 " 0 1 number_pixels 1 sub",
1215 " pixels exch gray_packet put",
1217 " pixels 0 number_pixels getinterval",
1220 "/PseudoClassPacket",
1223 " % Get a PseudoClass packet.",
1226 " % index: index into the colormap.",
1227 " % length: number of pixels minus one of this color (optional).",
1229 " currentfile byte readhexstring pop 0 get",
1230 " /offset exch 3 mul def",
1231 " /color_packet colormap offset 3 getinterval def",
1232 " compression 0 eq",
1234 " /number_pixels 3 def",
1237 " currentfile byte readhexstring pop 0 get",
1238 " /number_pixels exch 1 add 3 mul def",
1240 " 0 3 number_pixels 1 sub",
1242 " pixels exch color_packet putinterval",
1244 " pixels 0 number_pixels getinterval",
1247 "/PseudoClassImage",
1250 " % Display a PseudoClass image.",
1253 " % class: 0-PseudoClass or 1-Grayscale.",
1255 " currentfile buffer readline pop",
1256 " token pop /class exch def pop",
1259 " currentfile buffer readline pop",
1260 " token pop /depth exch def pop",
1261 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1262 " columns rows depth",
1267 " { currentfile grays readhexstring pop } image",
1272 " % colors: number of colors in the colormap.",
1273 " % colormap: red, green, blue color packets.",
1275 " currentfile buffer readline pop",
1276 " token pop /colors exch def pop",
1277 " /colors colors 3 mul def",
1278 " /colormap colors string def",
1279 " currentfile colormap readhexstring pop pop",
1280 " systemdict /colorimage known",
1287 " { PseudoClassPacket } false 3 colorimage",
1291 " % No colorimage operator; convert to grayscale.",
1298 " { GrayPseudoClassPacket } image",
1306 " % Display a DirectClass or PseudoClass image.",
1309 " % x & y translation.",
1311 " % label pointsize.",
1313 " % image columns & rows.",
1314 " % class: 0-DirectClass or 1-PseudoClass.",
1315 " % compression: 0-none or 1-RunlengthEncoded.",
1316 " % hex color packets.",
1319 " /buffer 512 string def",
1320 " /byte 1 string def",
1321 " /color_packet 3 string def",
1322 " /pixels 768 string def",
1324 " currentfile buffer readline pop",
1325 " token pop /x exch def",
1326 " token pop /y exch def pop",
1328 " currentfile buffer readline pop",
1329 " token pop /x exch def",
1330 " token pop /y exch def pop",
1331 " currentfile buffer readline pop",
1332 " token pop /pointsize exch def pop",
1333 " /Times-Roman findfont pointsize scalefont setfont",
1336 *PostscriptEpilog[]=
1339 " currentfile buffer readline pop",
1340 " token pop /columns exch def",
1341 " token pop /rows exch def pop",
1342 " currentfile buffer readline pop",
1343 " token pop /class exch def pop",
1344 " currentfile buffer readline pop",
1345 " token pop /compression exch def pop",
1346 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1351 buffer[MaxTextExtent],
1352 date[MaxTextExtent],
1354 page_geometry[MaxTextExtent];
1394 register const Quantum
1401 register unsigned char
1425 Open output image file.
1427 assert(image_info != (const ImageInfo *) NULL);
1428 assert(image_info->signature == MagickSignature);
1429 assert(image != (Image *) NULL);
1430 assert(image->signature == MagickSignature);
1431 if (image->debug != MagickFalse)
1432 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1433 assert(exception != (ExceptionInfo *) NULL);
1434 assert(exception->signature == MagickSignature);
1435 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1436 if (status == MagickFalse)
1438 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1444 Scale relative to dots-per-inch.
1446 if ((IsRGBColorspace(image->colorspace) == MagickFalse) &&
1447 (image->colorspace != CMYKColorspace))
1448 (void) TransformImageColorspace(image,RGBColorspace,exception);
1449 delta.x=DefaultResolution;
1450 delta.y=DefaultResolution;
1451 resolution.x=image->resolution.x;
1452 resolution.y=image->resolution.y;
1453 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1455 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1456 resolution.x=geometry_info.rho;
1457 resolution.y=geometry_info.sigma;
1458 if ((flags & SigmaValue) == 0)
1459 resolution.y=resolution.x;
1461 if (image_info->density != (char *) NULL)
1463 flags=ParseGeometry(image_info->density,&geometry_info);
1464 resolution.x=geometry_info.rho;
1465 resolution.y=geometry_info.sigma;
1466 if ((flags & SigmaValue) == 0)
1467 resolution.y=resolution.x;
1469 if (image->units == PixelsPerCentimeterResolution)
1471 resolution.x=(double) ((size_t) (100.0*2.54*resolution.x+0.5)/100.0);
1472 resolution.y=(double) ((size_t) (100.0*2.54*resolution.y+0.5)/100.0);
1474 SetGeometry(image,&geometry);
1475 (void) FormatLocaleString(page_geometry,MaxTextExtent,"%.20gx%.20g",
1476 (double) image->columns,(double) image->rows);
1477 if (image_info->page != (char *) NULL)
1478 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1480 if ((image->page.width != 0) && (image->page.height != 0))
1481 (void) FormatLocaleString(page_geometry,MaxTextExtent,
1482 "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,(double)
1483 image->page.height,(double) image->page.x,(double) image->page.y);
1485 if ((image->gravity != UndefinedGravity) &&
1486 (LocaleCompare(image_info->magick,"PS") == 0))
1487 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1488 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1489 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1490 &geometry.width,&geometry.height);
1491 scale.x=(double) (geometry.width*delta.x)/resolution.x;
1492 geometry.width=(size_t) floor(scale.x+0.5);
1493 scale.y=(double) (geometry.height*delta.y)/resolution.y;
1494 geometry.height=(size_t) floor(scale.y+0.5);
1495 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1496 (void) ParseGravityGeometry(image,page_geometry,&page_info,exception);
1497 if (image->gravity != UndefinedGravity)
1499 geometry.x=(-page_info.x);
1500 geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
1503 if (image_info->pointsize != 0.0)
1504 pointsize=image_info->pointsize;
1506 value=GetImageProperty(image,"label",exception);
1507 if (value != (const char *) NULL)
1508 text_size=(size_t) (MultilineCensus(value)*pointsize+12);
1512 Output Postscript header.
1514 if (LocaleCompare(image_info->magick,"PS") == 0)
1515 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1517 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1519 (void) WriteBlobString(image,buffer);
1520 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
1521 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
1523 (void) WriteBlobString(image,buffer);
1524 timer=time((time_t *) NULL);
1525 (void) FormatMagickTime(timer,MaxTextExtent,date);
1526 (void) FormatLocaleString(buffer,MaxTextExtent,
1527 "%%%%CreationDate: (%s)\n",date);
1528 (void) WriteBlobString(image,buffer);
1529 bounds.x1=(double) geometry.x;
1530 bounds.y1=(double) geometry.y;
1531 bounds.x2=(double) geometry.x+scale.x;
1532 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1533 if ((image_info->adjoin != MagickFalse) &&
1534 (GetNextImageInList(image) != (Image *) NULL))
1535 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1539 (void) FormatLocaleString(buffer,MaxTextExtent,
1540 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
1541 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
1542 (void) WriteBlobString(image,buffer);
1543 (void) FormatLocaleString(buffer,MaxTextExtent,
1544 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
1545 bounds.y1,bounds.x2,bounds.y2);
1547 (void) WriteBlobString(image,buffer);
1548 profile=GetImageProfile(image,"8bim");
1549 if (profile != (StringInfo *) NULL)
1552 Embed Photoshop profile.
1554 (void) FormatLocaleString(buffer,MaxTextExtent,
1555 "%%BeginPhotoshop: %.20g",(double) GetStringInfoLength(profile));
1556 (void) WriteBlobString(image,buffer);
1557 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1560 (void) WriteBlobString(image,"\n% ");
1561 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X",
1562 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1563 (void) WriteBlobString(image,buffer);
1565 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1567 profile=GetImageProfile(image,"xmp");
1568 if (0 && (profile != (StringInfo *) NULL))
1573 (void) WriteBlobString(image,"\n%begin_xml_code\n");
1574 (void) FormatLocaleString(buffer,MaxTextExtent,
1575 "\n%%begin_xml_packet: %.20g\n",(double)
1576 GetStringInfoLength(profile));
1577 (void) WriteBlobString(image,buffer);
1578 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
1579 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
1580 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
1582 value=GetImageProperty(image,"label",exception);
1583 if (value != (const char *) NULL)
1584 (void) WriteBlobString(image,
1585 "%%DocumentNeededResources: font Times-Roman\n");
1586 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1587 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1588 if (LocaleCompare(image_info->magick,"PS") != 0)
1589 (void) WriteBlobString(image,"%%Pages: 1\n");
1593 Compute the number of pages.
1595 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1596 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1597 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Pages: %.20g\n",
1598 image_info->adjoin != MagickFalse ? (double)
1599 GetImageListLength(image) : 1.0);
1600 (void) WriteBlobString(image,buffer);
1602 (void) WriteBlobString(image,"%%EndComments\n");
1603 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1604 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1605 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1606 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1607 (LocaleCompare(image_info->magick,"EPT") == 0))
1622 Create preview image.
1624 preview_image=CloneImage(image,0,0,MagickTrue,exception);
1625 if (preview_image == (Image *) NULL)
1626 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1628 Dump image as bitmap.
1630 (void) FormatLocaleString(buffer,MaxTextExtent,
1631 "%%%%BeginPreview: %.20g %.20g %.20g %.20g\n%% ",(double)
1632 preview_image->columns,(double) preview_image->rows,1.0,
1633 (double) ((((preview_image->columns+7) >> 3)*preview_image->rows+
1635 (void) WriteBlobString(image,buffer);
1637 for (y=0; y < (ssize_t) image->rows; y++)
1639 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1641 if (p == (const Quantum *) NULL)
1645 for (x=0; x < (ssize_t) preview_image->columns; x++)
1648 pixel=GetPixelIntensity(preview_image,p);
1649 if (pixel >= (Quantum) (QuantumRange/2))
1654 q=PopHexPixel(hex_digits,byte,q);
1655 if ((q-pixels+8) >= 80)
1658 (void) WriteBlob(image,q-pixels,pixels);
1660 (void) WriteBlobString(image,"% ");
1669 q=PopHexPixel(hex_digits,byte,q);
1670 if ((q-pixels+8) >= 80)
1673 (void) WriteBlob(image,q-pixels,pixels);
1675 (void) WriteBlobString(image,"% ");
1682 (void) WriteBlob(image,q-pixels,pixels);
1684 (void) WriteBlobString(image,"\n%%EndPreview\n");
1685 preview_image=DestroyImage(preview_image);
1688 Output Postscript commands.
1690 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1692 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
1693 (void) WriteBlobString(image,buffer);
1695 value=GetImageProperty(image,"label",exception);
1696 if (value != (const char *) NULL)
1697 for (j=(ssize_t) MultilineCensus(value)-1; j >= 0; j--)
1699 (void) WriteBlobString(image," /label 512 string def\n");
1700 (void) WriteBlobString(image," currentfile label readline pop\n");
1701 (void) FormatLocaleString(buffer,MaxTextExtent,
1702 " 0 y %g add moveto label show pop\n",j*pointsize+12);
1703 (void) WriteBlobString(image,buffer);
1705 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1707 (void) FormatLocaleString(buffer,MaxTextExtent,"%s\n",*s);
1708 (void) WriteBlobString(image,buffer);
1710 if (LocaleCompare(image_info->magick,"PS") == 0)
1711 (void) WriteBlobString(image," showpage\n");
1712 (void) WriteBlobString(image,"} bind def\n");
1713 (void) WriteBlobString(image,"%%EndProlog\n");
1715 (void) FormatLocaleString(buffer,MaxTextExtent,"%%%%Page: 1 %.20g\n",
1717 (void) WriteBlobString(image,buffer);
1718 (void) FormatLocaleString(buffer,MaxTextExtent,
1719 "%%%%PageBoundingBox: %.20g %.20g %.20g %.20g\n",(double) geometry.x,
1720 (double) geometry.y,geometry.x+(double) geometry.width,geometry.y+(double)
1721 (geometry.height+text_size));
1722 (void) WriteBlobString(image,buffer);
1723 if ((double) geometry.x < bounds.x1)
1724 bounds.x1=(double) geometry.x;
1725 if ((double) geometry.y < bounds.y1)
1726 bounds.y1=(double) geometry.y;
1727 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1728 bounds.x2=(double) geometry.x+geometry.width-1;
1729 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1730 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1731 value=GetImageProperty(image,"label",exception);
1732 if (value != (const char *) NULL)
1733 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1734 if (LocaleCompare(image_info->magick,"PS") != 0)
1735 (void) WriteBlobString(image,"userdict begin\n");
1736 (void) WriteBlobString(image,"DisplayImage\n");
1740 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n%g %g\n%g\n",
1741 (double) geometry.x,(double) geometry.y,scale.x,scale.y,pointsize);
1742 (void) WriteBlobString(image,buffer);
1743 labels=(char **) NULL;
1744 value=GetImageProperty(image,"label",exception);
1745 if (value != (const char *) NULL)
1746 labels=StringToList(value);
1747 if (labels != (char **) NULL)
1749 for (i=0; labels[i] != (char *) NULL; i++)
1751 (void) FormatLocaleString(buffer,MaxTextExtent,"%s \n",
1753 (void) WriteBlobString(image,buffer);
1754 labels[i]=DestroyString(labels[i]);
1756 labels=(char **) RelinquishMagickMemory(labels);
1758 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1759 pixel.alpha=(Quantum) TransparentAlpha;
1762 if ((image_info->type != TrueColorType) &&
1763 (IsImageGray(image,exception) != MagickFalse))
1765 if (IsImageMonochrome(image,exception) == MagickFalse)
1771 Dump image as grayscale.
1773 (void) FormatLocaleString(buffer,MaxTextExtent,
1774 "%.20g %.20g\n1\n1\n1\n8\n",(double) image->columns,(double)
1776 (void) WriteBlobString(image,buffer);
1778 for (y=0; y < (ssize_t) image->rows; y++)
1780 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1781 if (p == (const Quantum *) NULL)
1783 for (x=0; x < (ssize_t) image->columns; x++)
1785 pixel=(Quantum) ScaleQuantumToChar(GetPixelIntensity(image,p));
1786 q=PopHexPixel(hex_digits,(size_t) pixel,q);
1788 if ((q-pixels+8) >= 80)
1791 (void) WriteBlob(image,q-pixels,pixels);
1794 p+=GetPixelChannels(image);
1796 if (image->previous == (Image *) NULL)
1798 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1800 if (status == MagickFalse)
1807 (void) WriteBlob(image,q-pixels,pixels);
1819 Dump image as bitmap.
1821 (void) FormatLocaleString(buffer,MaxTextExtent,
1822 "%.20g %.20g\n1\n1\n1\n1\n",(double) image->columns,(double)
1824 (void) WriteBlobString(image,buffer);
1826 for (y=0; y < (ssize_t) image->rows; y++)
1828 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1829 if (p == (const Quantum *) NULL)
1833 for (x=0; x < (ssize_t) image->columns; x++)
1836 pixel=GetPixelIntensity(image,p);
1837 if (pixel >= (Quantum) (QuantumRange/2))
1842 q=PopHexPixel(hex_digits,byte,q);
1843 if ((q-pixels+2) >= 80)
1846 (void) WriteBlob(image,q-pixels,pixels);
1852 p+=GetPixelChannels(image);
1857 q=PopHexPixel(hex_digits,byte,q);
1858 if ((q-pixels+2) >= 80)
1861 (void) WriteBlob(image,q-pixels,pixels);
1865 if (image->previous == (Image *) NULL)
1867 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1869 if (status == MagickFalse)
1876 (void) WriteBlob(image,q-pixels,pixels);
1881 if ((image->storage_class == DirectClass) ||
1882 (image->colors > 256) || (image->matte != MagickFalse))
1885 Dump DirectClass image.
1887 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g %.20g\n0\n%d\n",
1888 (double) image->columns,(double) image->rows,
1889 image_info->compression == RLECompression ? 1 : 0);
1890 (void) WriteBlobString(image,buffer);
1891 switch (image_info->compression)
1893 case RLECompression:
1896 Dump runlength-encoded DirectColor packets.
1899 for (y=0; y < (ssize_t) image->rows; y++)
1901 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1902 if (p == (const Quantum *) NULL)
1904 GetPixelInfoPixel(image,p,&pixel);
1906 for (x=0; x < (ssize_t) image->columns; x++)
1908 if ((GetPixelRed(image,p) == pixel.red) &&
1909 (GetPixelGreen(image,p) == pixel.green) &&
1910 (GetPixelBlue(image,p) == pixel.blue) &&
1911 (GetPixelAlpha(image,p) == pixel.alpha) &&
1912 (length < 255) && (x < (ssize_t) (image->columns-1)))
1918 WriteRunlengthPacket(image,pixel,length,p);
1919 if ((q-pixels+10) >= 80)
1922 (void) WriteBlob(image,q-pixels,pixels);
1928 GetPixelInfoPixel(image,p,&pixel);
1929 p+=GetPixelChannels(image);
1931 WriteRunlengthPacket(image,pixel,length,p);
1932 if ((q-pixels+10) >= 80)
1935 (void) WriteBlob(image,q-pixels,pixels);
1938 if (image->previous == (Image *) NULL)
1940 status=SetImageProgress(image,SaveImageTag,
1941 (MagickOffsetType) y,image->rows);
1942 if (status == MagickFalse)
1949 (void) WriteBlob(image,q-pixels,pixels);
1957 Dump uncompressed DirectColor packets.
1960 for (y=0; y < (ssize_t) image->rows; y++)
1962 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1963 if (p == (const Quantum *) NULL)
1965 for (x=0; x < (ssize_t) image->columns; x++)
1967 if ((image->matte != MagickFalse) &&
1968 (GetPixelAlpha(image,p) == (Quantum) TransparentAlpha))
1970 q=PopHexPixel(hex_digits,0xff,q);
1971 q=PopHexPixel(hex_digits,0xff,q);
1972 q=PopHexPixel(hex_digits,0xff,q);
1976 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1977 GetPixelRed(image,p)),q);
1978 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1979 GetPixelGreen(image,p)),q);
1980 q=PopHexPixel(hex_digits,ScaleQuantumToChar(
1981 GetPixelBlue(image,p)),q);
1983 if ((q-pixels+6) >= 80)
1986 (void) WriteBlob(image,q-pixels,pixels);
1989 p+=GetPixelChannels(image);
1991 if (image->previous == (Image *) NULL)
1993 status=SetImageProgress(image,SaveImageTag,
1994 (MagickOffsetType) y,image->rows);
1995 if (status == MagickFalse)
2002 (void) WriteBlob(image,q-pixels,pixels);
2007 (void) WriteBlobByte(image,'\n');
2012 Dump PseudoClass image.
2014 (void) FormatLocaleString(buffer,MaxTextExtent,
2015 "%.20g %.20g\n%d\n%d\n0\n",(double) image->columns,(double)
2016 image->rows,image->storage_class == PseudoClass ? 1 : 0,
2017 image_info->compression == RLECompression ? 1 : 0);
2018 (void) WriteBlobString(image,buffer);
2020 Dump number of colors and colormap.
2022 (void) FormatLocaleString(buffer,MaxTextExtent,"%.20g\n",(double)
2024 (void) WriteBlobString(image,buffer);
2025 for (i=0; i < (ssize_t) image->colors; i++)
2027 (void) FormatLocaleString(buffer,MaxTextExtent,"%02X%02X%02X\n",
2028 ScaleQuantumToChar(image->colormap[i].red),
2029 ScaleQuantumToChar(image->colormap[i].green),
2030 ScaleQuantumToChar(image->colormap[i].blue));
2031 (void) WriteBlobString(image,buffer);
2033 switch (image_info->compression)
2035 case RLECompression:
2038 Dump runlength-encoded PseudoColor packets.
2041 for (y=0; y < (ssize_t) image->rows; y++)
2043 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2044 if (p == (const Quantum *) NULL)
2046 index=GetPixelIndex(image,p);
2048 for (x=0; x < (ssize_t) image->columns; x++)
2050 if ((index == GetPixelIndex(image,p)) &&
2051 (length < 255) && (x < ((ssize_t) image->columns-1)))
2057 q=PopHexPixel(hex_digits,(size_t) index,q);
2058 q=PopHexPixel(hex_digits,(size_t)
2059 MagickMin(length,0xff),q);
2061 if ((q-pixels+6) >= 80)
2064 (void) WriteBlob(image,q-pixels,pixels);
2070 index=GetPixelIndex(image,p);
2071 pixel.red=GetPixelRed(image,p);
2072 pixel.green=GetPixelGreen(image,p);
2073 pixel.blue=GetPixelBlue(image,p);
2074 pixel.alpha=GetPixelAlpha(image,p);
2075 p+=GetPixelChannels(image);
2077 q=PopHexPixel(hex_digits,(size_t) index,q);
2078 q=PopHexPixel(hex_digits,(size_t)
2079 MagickMin(length,0xff),q);
2080 if (image->previous == (Image *) NULL)
2082 status=SetImageProgress(image,SaveImageTag,
2083 (MagickOffsetType) y,image->rows);
2084 if (status == MagickFalse)
2091 (void) WriteBlob(image,q-pixels,pixels);
2099 Dump uncompressed PseudoColor packets.
2102 for (y=0; y < (ssize_t) image->rows; y++)
2104 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2105 if (p == (const Quantum *) NULL)
2107 for (x=0; x < (ssize_t) image->columns; x++)
2109 q=PopHexPixel(hex_digits,(size_t) GetPixelIndex(image,p),q);
2110 if ((q-pixels+4) >= 80)
2113 (void) WriteBlob(image,q-pixels,pixels);
2116 p+=GetPixelChannels(image);
2118 if (image->previous == (Image *) NULL)
2120 status=SetImageProgress(image,SaveImageTag,
2121 (MagickOffsetType) y,image->rows);
2122 if (status == MagickFalse)
2129 (void) WriteBlob(image,q-pixels,pixels);
2134 (void) WriteBlobByte(image,'\n');
2136 if (LocaleCompare(image_info->magick,"PS") != 0)
2137 (void) WriteBlobString(image,"end\n");
2138 (void) WriteBlobString(image,"%%PageTrailer\n");
2139 if (GetNextImageInList(image) == (Image *) NULL)
2141 image=SyncNextImageInList(image);
2142 status=SetImageProgress(image,SaveImagesTag,scene++,
2143 GetImageListLength(image));
2144 if (status == MagickFalse)
2146 } while (image_info->adjoin != MagickFalse);
2147 (void) WriteBlobString(image,"%%Trailer\n");
2150 (void) FormatLocaleString(buffer,MaxTextExtent,
2151 "%%%%BoundingBox: %.20g %.20g %.20g %.20g\n",ceil(bounds.x1-0.5),
2152 ceil(bounds.y1-0.5),floor(bounds.x2+0.5),floor(bounds.y2+0.5));
2153 (void) WriteBlobString(image,buffer);
2154 (void) FormatLocaleString(buffer,MaxTextExtent,
2155 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
2156 bounds.x2,bounds.y2);
2157 (void) WriteBlobString(image,buffer);
2159 (void) WriteBlobString(image,"%%EOF\n");
2160 (void) CloseBlob(image);