2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Postscript Format %
20 % Copyright 1999-2010 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 "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/blob-private.h"
45 #include "magick/cache.h"
46 #include "magick/color.h"
47 #include "magick/color-private.h"
48 #include "magick/colorspace.h"
49 #include "magick/constitute.h"
50 #include "magick/delegate.h"
51 #include "magick/delegate-private.h"
52 #include "magick/draw.h"
53 #include "magick/exception.h"
54 #include "magick/exception-private.h"
55 #include "magick/geometry.h"
56 #include "magick/image.h"
57 #include "magick/image-private.h"
58 #include "magick/list.h"
59 #include "magick/magick.h"
60 #include "magick/memory_.h"
61 #include "magick/monitor.h"
62 #include "magick/monitor-private.h"
63 #include "magick/option.h"
64 #include "magick/profile.h"
65 #include "magick/resource_.h"
66 #include "magick/pixel-private.h"
67 #include "magick/property.h"
68 #include "magick/quantum-private.h"
69 #include "magick/static.h"
70 #include "magick/string_.h"
71 #include "magick/module.h"
72 #include "magick/token.h"
73 #include "magick/transform.h"
74 #include "magick/utility.h"
79 static MagickBooleanType
80 WritePSImage(const ImageInfo *,Image *);
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 % I n v o k e P o s t s r i p t D e l e g a t e %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 % InvokePostscriptDelegate() executes the Postscript interpreter with the
96 % The format of the InvokePostscriptDelegate method is:
98 % MagickBooleanType InvokePostscriptDelegate(
99 % const MagickBooleanType verbose,const char *command,
100 % ExceptionInfo *exception)
102 % A description of each parameter follows:
104 % o verbose: A value other than zero displays the command prior to
107 % o command: the address of a character string containing the command to
110 % o exception: return any errors or warnings in this structure.
113 static MagickBooleanType InvokePostscriptDelegate(
114 const MagickBooleanType verbose,const char *command,ExceptionInfo *exception)
119 #if defined(MAGICKCORE_GS_DELEGATE) || defined(__WINDOWS__)
136 #if defined(__WINDOWS__)
137 ghost_info=NTGhostscriptDLLVectors();
142 ghost_info=(&ghost_info_struct);
143 (void) ResetMagickMemory(&ghost_info,0,sizeof(ghost_info));
144 ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
146 ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
147 gsapi_init_with_args;
148 ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
149 int *)) gsapi_run_string;
150 ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
151 gsapi_delete_instance;
152 ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
154 if (ghost_info == (GhostInfo *) NULL)
156 status=SystemCommand(MagickFalse,verbose,command,exception);
157 return(status == 0 ? MagickTrue : MagickFalse);
159 if (verbose != MagickFalse)
161 (void) fputs("[ghostscript library]",stdout);
162 (void) fputs(strchr(command,' '),stdout);
164 status=(ghost_info->new_instance)(&interpreter,(void *) NULL);
167 status=SystemCommand(MagickFalse,verbose,command,exception);
168 return(status == 0 ? MagickTrue : MagickFalse);
170 argv=StringToArgv(command,&argc);
171 status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
173 status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
175 (ghost_info->exit)(interpreter);
176 (ghost_info->delete_instance)(interpreter);
177 #if defined(__WINDOWS__)
178 NTGhostscriptUnLoadDLL();
180 for (i=0; i < (long) argc; i++)
181 argv[i]=DestroyString(argv[i]);
182 argv=(char **) RelinquishMagickMemory(argv);
183 if ((status != 0) && (status != -101))
188 message=GetExceptionMessage(errno);
189 (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
190 "`%s': %s",command,message);
191 message=DestroyString(message);
192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
193 "Ghostscript returns status %d, exit code %d",status,code);
198 status=SystemCommand(MagickFalse,verbose,command,exception);
199 return(status == 0 ? MagickTrue : MagickFalse);
204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
214 % IsPS() returns MagickTrue if the image format type, identified by the
215 % magick string, is PS.
217 % The format of the IsPS method is:
219 % MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
221 % A description of each parameter follows:
223 % o magick: compare image format pattern against these bytes.
225 % o length: Specifies the length of the magick string.
228 static MagickBooleanType IsPS(const unsigned char *magick,const size_t length)
232 if (memcmp(magick,"%!",2) == 0)
234 if (memcmp(magick,"\004%!",3) == 0)
240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244 % R e a d P S I m a g e %
248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250 % ReadPSImage() reads a Postscript image file and returns it. It allocates
251 % the memory necessary for the new Image structure and returns a pointer
254 % The format of the ReadPSImage method is:
256 % Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
258 % A description of each parameter follows:
260 % o image_info: the image info.
262 % o exception: return any errors or warnings in this structure.
266 static MagickBooleanType IsPostscriptRendered(const char *path)
274 if ((path == (const char *) NULL) || (*path == '\0'))
276 status=GetPathAttributes(path,&attributes);
277 if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
278 (attributes.st_size > 0))
283 static inline int ProfileInteger(Image *image,short int *hex_digits)
297 c=ReadBlobByte(image);
298 if ((c == EOF) || ((c == '%') && (l == '%')))
305 if (isxdigit(c) == MagickFalse)
307 value=(int) ((unsigned long) value << 4)+hex_digits[c];
313 static Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
315 #define BoundingBox "BoundingBox:"
316 #define BeginDocument "BeginDocument:"
317 #define BeginXMPPacket "<?xpacket begin="
318 #define EndXMPPacket "<?xpacket end="
319 #define ICCProfile "BeginICCProfile:"
320 #define CMYKCustomColor "CMYKCustomColor:"
321 #define DocumentMedia "DocumentMedia:"
322 #define DocumentCustomColors "DocumentCustomColors:"
323 #define DocumentProcessColors "DocumentProcessColors:"
324 #define EndDocument "EndDocument:"
325 #define HiResBoundingBox "HiResBoundingBox:"
326 #define ImageData "ImageData:"
327 #define PageBoundingBox "PageBoundingBox:"
328 #define LanguageLevel "LanguageLevel:"
329 #define PageMedia "PageMedia:"
330 #define Pages "Pages:"
331 #define PhotoshopProfile "BeginPhotoshop:"
332 #define PostscriptLevel "!PS-"
333 #define RenderPostscriptText " Rendering Postscript... "
334 #define SpotColor "+ "
337 command[MaxTextExtent],
338 density[MaxTextExtent],
339 filename[MaxTextExtent],
340 geometry[MaxTextExtent],
341 input_filename[MaxTextExtent],
342 options[MaxTextExtent],
343 postscript_filename[MaxTextExtent],
344 translate_geometry[MaxTextExtent];
415 assert(image_info != (const ImageInfo *) NULL);
416 assert(image_info->signature == MagickSignature);
417 if (image_info->debug != MagickFalse)
418 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
419 image_info->filename);
420 assert(exception != (ExceptionInfo *) NULL);
421 assert(exception->signature == MagickSignature);
422 image=AcquireImage(image_info);
423 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
424 if (status == MagickFalse)
426 image=DestroyImageList(image);
427 return((Image *) NULL);
429 status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
430 if (status == MagickFalse)
432 ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
433 image_info->filename);
434 image=DestroyImageList(image);
435 return((Image *) NULL);
438 Initialize hex values.
440 (void) ResetMagickMemory(hex_digits,0,sizeof(hex_digits));
441 hex_digits[(int) '0']=0;
442 hex_digits[(int) '1']=1;
443 hex_digits[(int) '2']=2;
444 hex_digits[(int) '3']=3;
445 hex_digits[(int) '4']=4;
446 hex_digits[(int) '5']=5;
447 hex_digits[(int) '6']=6;
448 hex_digits[(int) '7']=7;
449 hex_digits[(int) '8']=8;
450 hex_digits[(int) '9']=9;
451 hex_digits[(int) 'a']=10;
452 hex_digits[(int) 'b']=11;
453 hex_digits[(int) 'c']=12;
454 hex_digits[(int) 'd']=13;
455 hex_digits[(int) 'e']=14;
456 hex_digits[(int) 'f']=15;
457 hex_digits[(int) 'A']=10;
458 hex_digits[(int) 'B']=11;
459 hex_digits[(int) 'C']=12;
460 hex_digits[(int) 'D']=13;
461 hex_digits[(int) 'E']=14;
462 hex_digits[(int) 'F']=15;
464 Set the page density.
466 delta.x=DefaultResolution;
467 delta.y=DefaultResolution;
468 if ((image->x_resolution == 0.0) || (image->y_resolution == 0.0))
470 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
471 image->x_resolution=geometry_info.rho;
472 image->y_resolution=geometry_info.sigma;
473 if ((flags & SigmaValue) == 0)
474 image->y_resolution=image->x_resolution;
477 Determine page geometry from the Postscript bounding box.
479 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
480 (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
481 (void) ResetMagickMemory(&page,0,sizeof(page));
482 (void) ResetMagickMemory(command,0,sizeof(command));
491 cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
494 for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
497 Note document structuring comments.
500 if ((strchr("\n\r%",c) == (char *) NULL) &&
501 ((size_t) (p-command) < (MaxTextExtent-1)))
506 Skip %%BeginDocument thru %%EndDocument.
508 if (LocaleNCompare(BeginDocument,command,strlen(BeginDocument)) == 0)
510 if (LocaleNCompare(EndDocument,command,strlen(EndDocument)) == 0)
512 if (skip != MagickFalse)
514 if (LocaleNCompare(PostscriptLevel,command,strlen(PostscriptLevel)) == 0)
516 (void) SetImageProperty(image,"ps:Level",command+4);
517 if (GlobExpression(command,"*EPSF-*",MagickTrue) != MagickFalse)
520 if (LocaleNCompare(LanguageLevel,command,strlen(LanguageLevel)) == 0)
521 (void) sscanf(command,LanguageLevel " %lu",&language_level);
522 if (LocaleNCompare(Pages,command,strlen(Pages)) == 0)
523 (void) sscanf(command,Pages " %lu",&pages);
524 if (LocaleNCompare(ImageData,command,strlen(Pages)) == 0)
525 (void) sscanf(command,ImageData " %lu %lu",&columns,&rows);
526 if (LocaleNCompare(ICCProfile,command,strlen(PhotoshopProfile)) == 0)
534 profile=AcquireStringInfo(65536);
535 for (i=0; (c=ProfileInteger(image,hex_digits)) != EOF; i++)
537 SetStringInfoLength(profile,(size_t) i+1);
538 datum=GetStringInfoDatum(profile);
539 datum[i]=(unsigned char) c;
541 (void) SetImageProfile(image,"icc",profile);
542 profile=DestroyStringInfo(profile);
545 if (LocaleNCompare(PhotoshopProfile,command,strlen(PhotoshopProfile)) == 0)
551 Read Photoshop profile.
553 count=(ssize_t) sscanf(command,PhotoshopProfile " %lu",&extent);
557 profile=AcquireStringInfo(length);
558 p=GetStringInfoDatum(profile);
559 for (i=0; i < (long) length; i++)
560 *p++=(unsigned char) ProfileInteger(image,hex_digits);
561 (void) SetImageProfile(image,"8bim",profile);
562 profile=DestroyStringInfo(profile);
565 if (LocaleNCompare(BeginXMPPacket,command,strlen(BeginXMPPacket)) == 0)
574 profile=StringToStringInfo(command);
575 for (i=GetStringInfoLength(profile)-1; c != EOF; i++)
577 SetStringInfoLength(profile,i+1);
578 c=ReadBlobByte(image);
579 GetStringInfoDatum(profile)[i]=(unsigned char) c;
581 if ((strchr("\n\r%",c) == (char *) NULL) &&
582 ((size_t) (p-command) < (MaxTextExtent-1)))
586 if (LocaleNCompare(EndXMPPacket,command,strlen(EndXMPPacket)) == 0)
589 SetStringInfoLength(profile,i);
590 (void) SetImageProfile(image,"xmp",profile);
591 profile=DestroyStringInfo(profile);
595 Is this a CMYK document?
597 length=strlen(DocumentProcessColors);
598 if (LocaleNCompare(DocumentProcessColors,command,length) == 0)
600 if ((GlobExpression(command,"*Cyan*",MagickTrue) != MagickFalse) ||
601 (GlobExpression(command,"*Magenta*",MagickTrue) != MagickFalse) ||
602 (GlobExpression(command,"*Yellow*",MagickTrue) != MagickFalse))
605 if (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0)
607 length=strlen(DocumentCustomColors);
608 if ((LocaleNCompare(DocumentCustomColors,command,length) == 0) ||
609 (LocaleNCompare(CMYKCustomColor,command,strlen(CMYKCustomColor)) == 0) ||
610 (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0))
613 property[MaxTextExtent],
622 (void) FormatMagickString(property,MaxTextExtent,"ps:SpotColor-%lu",
624 for (p=command; *p != '\0'; p++)
625 if (isspace((int) (unsigned char) *p) != 0)
627 value=AcquireString(p);
628 (void) SubstituteString(&value,"(","");
629 (void) SubstituteString(&value,")","");
630 (void) StripString(value);
631 (void) SetImageProperty(image,property,value);
632 value=DestroyString(value);
636 Note region defined by bounding box.
639 if (LocaleNCompare(BoundingBox,command,strlen(BoundingBox)) == 0)
640 count=(ssize_t) sscanf(command,BoundingBox " %lf %lf %lf %lf",&bounds.x1,
641 &bounds.y1,&bounds.x2,&bounds.y2);
642 if (LocaleNCompare(DocumentMedia,command,strlen(DocumentMedia)) == 0)
643 count=(ssize_t) sscanf(command,DocumentMedia " %*s %lf %lf",&bounds.x2,
645 if (LocaleNCompare(HiResBoundingBox,command,strlen(HiResBoundingBox)) == 0)
646 count=(ssize_t) sscanf(command,HiResBoundingBox " %lf %lf %lf %lf",
647 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
648 if (LocaleNCompare(PageBoundingBox,command,strlen(PageBoundingBox)) == 0)
649 count=(ssize_t) sscanf(command,PageBoundingBox " %lf %lf %lf %lf",
650 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
651 if (LocaleNCompare(PageMedia,command,strlen(PageMedia)) == 0)
652 count=(ssize_t) sscanf(command,PageMedia " %*s %lf %lf",&bounds.x2,
656 if (((bounds.x2 > hires_bounds.x2) && (bounds.y2 > hires_bounds.y2)) ||
657 ((hires_bounds.x2 == 0.0) && (hires_bounds.y2 == 0.0)))
660 Set Postscript render geometry.
662 (void) FormatMagickString(geometry,MaxTextExtent,
663 "%gx%g%+.15g%+.15g",bounds.x2-bounds.x1,bounds.y2-bounds.y1,
664 bounds.x1,bounds.y1);
665 (void) SetImageProperty(image,"ps:HiResBoundingBox",geometry);
666 page.width=(unsigned long) (bounds.x2-bounds.x1+0.5);
667 page.height=(unsigned long) (bounds.y2-bounds.y1+0.5);
671 (void) CloseBlob(image);
672 if (image_info->colorspace == RGBColorspace)
675 Create Ghostscript control file.
677 file=AcquireUniqueFileResource(postscript_filename);
680 ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
681 image_info->filename);
682 image=DestroyImageList(image);
683 return((Image *) NULL);
685 (void) CopyMagickString(command,"/setpagedevice {pop} bind 1 index where {"
686 "dup wcheck {3 1 roll put} {pop def} ifelse} {def} ifelse\n"
687 "<</UseCIEColor true>>setpagedevice\n",MaxTextExtent);
688 count=write(file,command,(unsigned int) strlen(command));
689 (void) FormatMagickString(translate_geometry,MaxTextExtent,
690 "%g %g translate\n",-bounds.x1,-bounds.y1);
691 count=write(file,translate_geometry,strlen(translate_geometry));
694 Render Postscript with the Ghostscript delegate.
696 if (image_info->monochrome != MagickFalse)
697 delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
699 if (cmyk != MagickFalse)
700 delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
703 delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
705 delegate_info=GetDelegateInfo("ps:color",(char *) NULL,exception);
706 if (delegate_info == (const DelegateInfo *) NULL)
708 (void) RelinquishUniqueFileResource(postscript_filename);
709 image=DestroyImageList(image);
710 return((Image *) NULL);
713 if ((page.width == 0) || (page.height == 0))
714 (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
715 if (image_info->page != (char *) NULL)
716 (void) ParseAbsoluteGeometry(image_info->page,&page);
717 (void) FormatMagickString(density,MaxTextExtent,"%gx%g",
718 image->x_resolution,image->y_resolution);
719 page.width=(unsigned long) (page.width*image->x_resolution/delta.x+0.5);
720 page.height=(unsigned long) (page.height*image->y_resolution/delta.y+0.5);
721 (void) FormatMagickString(options,MaxTextExtent,"-g%lux%lu ",
722 page.width,page.height);
723 read_info=CloneImageInfo(image_info);
724 *read_info->magick='\0';
725 if (read_info->number_scenes != 0)
728 pages[MaxTextExtent];
730 (void) FormatMagickString(pages,MaxTextExtent,"-dFirstPage=%lu "
731 "-dLastPage=%lu",read_info->scene+1,read_info->scene+
732 read_info->number_scenes);
733 (void) ConcatenateMagickString(options,pages,MaxTextExtent);
734 read_info->number_scenes=0;
735 if (read_info->scenes != (char *) NULL)
736 *read_info->scenes='\0';
738 option=GetImageOption(image_info,"ps:use-cropbox");
739 if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse))
740 (void) ConcatenateMagickString(options,"-dEPSCrop ",MaxTextExtent);
741 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
742 (void) AcquireUniqueFilename(read_info->filename);
743 (void) FormatMagickString(command,MaxTextExtent,
744 GetDelegateCommands(delegate_info),
745 read_info->antialias != MagickFalse ? 4 : 1,
746 read_info->antialias != MagickFalse ? 4 : 1,density,options,
747 read_info->filename,postscript_filename,input_filename);
748 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
749 if ((status == MagickFalse) ||
750 (IsPostscriptRendered(read_info->filename) == MagickFalse))
752 (void) ConcatenateMagickString(command," -c showpage",MaxTextExtent);
753 status=InvokePostscriptDelegate(read_info->verbose,command,exception);
755 postscript_image=(Image *) NULL;
756 if (status != MagickFalse)
757 postscript_image=ReadImage(read_info,exception);
758 (void) RelinquishUniqueFileResource(postscript_filename);
759 (void) RelinquishUniqueFileResource(read_info->filename);
760 (void) RelinquishUniqueFileResource(input_filename);
761 read_info=DestroyImageInfo(read_info);
762 if (postscript_image == (Image *) NULL)
764 image=DestroyImageList(image);
765 ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
766 image_info->filename);
767 return((Image *) NULL);
769 if (LocaleCompare(postscript_image->magick,"BMP") == 0)
774 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
775 if (cmyk_image != (Image *) NULL)
777 postscript_image=DestroyImageList(postscript_image);
778 postscript_image=cmyk_image;
781 if (image_info->number_scenes != 0)
790 Add place holder images to meet the subimage specification requirement.
792 for (i=0; i < (long) image_info->scene; i++)
794 clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
795 if (clone_image != (Image *) NULL)
796 PrependImageToList(&postscript_image,clone_image);
801 (void) CopyMagickString(postscript_image->filename,filename,MaxTextExtent);
803 postscript_image->magick_columns=columns;
805 postscript_image->magick_rows=rows;
806 postscript_image->page=page;
807 (void) CloneImageProfiles(postscript_image,image);
808 (void) CloneImageProperties(postscript_image,image);
809 next=SyncNextImageInList(postscript_image);
810 if (next != (Image *) NULL)
811 postscript_image=next;
812 } while (next != (Image *) NULL);
813 image=DestroyImageList(image);
815 for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
818 next=GetNextImageInList(next);
820 return(GetFirstImageInList(postscript_image));
824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
828 % R e g i s t e r P S I m a g e %
832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
834 % RegisterPSImage() adds properties for the PS image format to
835 % the list of supported formats. The properties include the image format
836 % tag, a method to read and/or write the format, whether the format
837 % supports the saving of more than one frame to the same file or blob,
838 % whether the format supports native in-memory I/O, and a brief
839 % description of the format.
841 % The format of the RegisterPSImage method is:
843 % unsigned long RegisterPSImage(void)
846 ModuleExport unsigned long RegisterPSImage(void)
851 entry=SetMagickInfo("EPI");
852 entry->decoder=(DecodeImageHandler *) ReadPSImage;
853 entry->encoder=(EncodeImageHandler *) WritePSImage;
854 entry->magick=(IsImageFormatHandler *) IsPS;
855 entry->adjoin=MagickFalse;
856 entry->blob_support=MagickFalse;
857 entry->seekable_stream=MagickTrue;
858 entry->thread_support=EncoderThreadSupport;
859 entry->description=ConstantString(
860 "Encapsulated PostScript Interchange format");
861 entry->module=ConstantString("PS");
862 (void) RegisterMagickInfo(entry);
863 entry=SetMagickInfo("EPS");
864 entry->decoder=(DecodeImageHandler *) ReadPSImage;
865 entry->encoder=(EncodeImageHandler *) WritePSImage;
866 entry->magick=(IsImageFormatHandler *) IsPS;
867 entry->adjoin=MagickFalse;
868 entry->blob_support=MagickFalse;
869 entry->seekable_stream=MagickTrue;
870 entry->thread_support=EncoderThreadSupport;
871 entry->description=ConstantString("Encapsulated PostScript");
872 entry->module=ConstantString("PS");
873 (void) RegisterMagickInfo(entry);
874 entry=SetMagickInfo("EPSF");
875 entry->decoder=(DecodeImageHandler *) ReadPSImage;
876 entry->encoder=(EncodeImageHandler *) WritePSImage;
877 entry->magick=(IsImageFormatHandler *) IsPS;
878 entry->adjoin=MagickFalse;
879 entry->blob_support=MagickFalse;
880 entry->seekable_stream=MagickTrue;
881 entry->description=ConstantString("Encapsulated PostScript");
882 entry->module=ConstantString("PS");
883 (void) RegisterMagickInfo(entry);
884 entry=SetMagickInfo("EPSI");
885 entry->decoder=(DecodeImageHandler *) ReadPSImage;
886 entry->encoder=(EncodeImageHandler *) WritePSImage;
887 entry->magick=(IsImageFormatHandler *) IsPS;
888 entry->adjoin=MagickFalse;
889 entry->blob_support=MagickFalse;
890 entry->seekable_stream=MagickTrue;
891 entry->thread_support=EncoderThreadSupport;
892 entry->description=ConstantString(
893 "Encapsulated PostScript Interchange format");
894 entry->module=ConstantString("PS");
895 (void) RegisterMagickInfo(entry);
896 entry=SetMagickInfo("PS");
897 entry->decoder=(DecodeImageHandler *) ReadPSImage;
898 entry->encoder=(EncodeImageHandler *) WritePSImage;
899 entry->magick=(IsImageFormatHandler *) IsPS;
900 entry->module=ConstantString("PS");
901 entry->blob_support=MagickFalse;
902 entry->seekable_stream=MagickTrue;
903 entry->thread_support=EncoderThreadSupport;
904 entry->description=ConstantString("PostScript");
905 (void) RegisterMagickInfo(entry);
906 return(MagickImageCoderSignature);
910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
914 % U n r e g i s t e r P S I m a g e %
918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920 % UnregisterPSImage() removes format registrations made by the
921 % PS module from the list of supported formats.
923 % The format of the UnregisterPSImage method is:
925 % UnregisterPSImage(void)
928 ModuleExport void UnregisterPSImage(void)
930 (void) UnregisterMagickInfo("EPI");
931 (void) UnregisterMagickInfo("EPS");
932 (void) UnregisterMagickInfo("EPSF");
933 (void) UnregisterMagickInfo("EPSI");
934 (void) UnregisterMagickInfo("PS");
938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
942 % W r i t e P S I m a g e %
946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
948 % WritePSImage translates an image to encapsulated Postscript
949 % Level I for printing. If the supplied geometry is null, the image is
950 % centered on the Postscript page. Otherwise, the image is positioned as
951 % specified by the geometry.
953 % The format of the WritePSImage method is:
955 % MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
957 % A description of each parameter follows:
959 % o image_info: the image info.
961 % o image: the image.
965 static inline size_t MagickMin(const size_t x,const size_t y)
972 static inline unsigned char *PopHexPixel(const char **hex_digits,
973 const unsigned long pixel,unsigned char *pixels)
978 hex=hex_digits[pixel];
979 *pixels++=(unsigned char) (*hex++);
980 *pixels++=(unsigned char) (*hex);
984 static MagickBooleanType WritePSImage(const ImageInfo *image_info,Image *image)
986 #define WriteRunlengthPacket(image,pixel,length,p) \
988 if ((image->matte != MagickFalse) && \
989 (p->opacity == (Quantum) TransparentOpacity)) \
991 q=PopHexPixel(hex_digits,0xff,q); \
992 q=PopHexPixel(hex_digits,0xff,q); \
993 q=PopHexPixel(hex_digits,0xff,q); \
997 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.red),q); \
998 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.green),q); \
999 q=PopHexPixel(hex_digits,ScaleQuantumToChar(pixel.blue),q); \
1001 q=PopHexPixel(hex_digits,(const unsigned long) MagickMin(length,0xff),q); \
1007 "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B",
1008 "0C", "0D", "0E", "0F", "10", "11", "12", "13", "14", "15", "16", "17",
1009 "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23",
1010 "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
1011 "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B",
1012 "3C", "3D", "3E", "3F", "40", "41", "42", "43", "44", "45", "46", "47",
1013 "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", "50", "51", "52", "53",
1014 "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
1015 "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B",
1016 "6C", "6D", "6E", "6F", "70", "71", "72", "73", "74", "75", "76", "77",
1017 "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", "80", "81", "82", "83",
1018 "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
1019 "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B",
1020 "9C", "9D", "9E", "9F", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7",
1021 "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", "B0", "B1", "B2", "B3",
1022 "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
1023 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB",
1024 "CC", "CD", "CE", "CF", "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7",
1025 "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", "E0", "E1", "E2", "E3",
1026 "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
1027 "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB",
1028 "FC", "FD", "FE", "FF", (char *) NULL
1030 *PostscriptProlog[]=
1034 "% Display a color image. The image is displayed in color on",
1035 "% Postscript viewers or printers that support color, otherwise",
1036 "% it is displayed as grayscale.",
1038 "/DirectClassPacket",
1041 " % Get a DirectClass packet.",
1047 " % length: number of pixels minus one of this color (optional).",
1049 " currentfile color_packet readhexstring pop pop",
1050 " compression 0 eq",
1052 " /number_pixels 3 def",
1055 " currentfile byte readhexstring pop 0 get",
1056 " /number_pixels exch 1 add 3 mul def",
1058 " 0 3 number_pixels 1 sub",
1060 " pixels exch color_packet putinterval",
1062 " pixels 0 number_pixels getinterval",
1065 "/DirectClassImage",
1068 " % Display a DirectClass image.",
1070 " systemdict /colorimage known",
1077 " { DirectClassPacket } false 3 colorimage",
1081 " % No colorimage operator; convert to grayscale.",
1088 " { GrayDirectClassPacket } image",
1092 "/GrayDirectClassPacket",
1095 " % Get a DirectClass packet; convert to grayscale.",
1101 " % length: number of pixels minus one of this color (optional).",
1103 " currentfile color_packet readhexstring pop pop",
1104 " color_packet 0 get 0.299 mul",
1105 " color_packet 1 get 0.587 mul add",
1106 " color_packet 2 get 0.114 mul add",
1108 " /gray_packet exch def",
1109 " compression 0 eq",
1111 " /number_pixels 1 def",
1114 " currentfile byte readhexstring pop 0 get",
1115 " /number_pixels exch 1 add def",
1117 " 0 1 number_pixels 1 sub",
1119 " pixels exch gray_packet put",
1121 " pixels 0 number_pixels getinterval",
1124 "/GrayPseudoClassPacket",
1127 " % Get a PseudoClass packet; convert to grayscale.",
1130 " % index: index into the colormap.",
1131 " % length: number of pixels minus one of this color (optional).",
1133 " currentfile byte readhexstring pop 0 get",
1134 " /offset exch 3 mul def",
1135 " /color_packet colormap offset 3 getinterval def",
1136 " color_packet 0 get 0.299 mul",
1137 " color_packet 1 get 0.587 mul add",
1138 " color_packet 2 get 0.114 mul add",
1140 " /gray_packet exch def",
1141 " compression 0 eq",
1143 " /number_pixels 1 def",
1146 " currentfile byte readhexstring pop 0 get",
1147 " /number_pixels exch 1 add def",
1149 " 0 1 number_pixels 1 sub",
1151 " pixels exch gray_packet put",
1153 " pixels 0 number_pixels getinterval",
1156 "/PseudoClassPacket",
1159 " % Get a PseudoClass packet.",
1162 " % index: index into the colormap.",
1163 " % length: number of pixels minus one of this color (optional).",
1165 " currentfile byte readhexstring pop 0 get",
1166 " /offset exch 3 mul def",
1167 " /color_packet colormap offset 3 getinterval def",
1168 " compression 0 eq",
1170 " /number_pixels 3 def",
1173 " currentfile byte readhexstring pop 0 get",
1174 " /number_pixels exch 1 add 3 mul def",
1176 " 0 3 number_pixels 1 sub",
1178 " pixels exch color_packet putinterval",
1180 " pixels 0 number_pixels getinterval",
1183 "/PseudoClassImage",
1186 " % Display a PseudoClass image.",
1189 " % class: 0-PseudoClass or 1-Grayscale.",
1191 " currentfile buffer readline pop",
1192 " token pop /class exch def pop",
1195 " currentfile buffer readline pop",
1196 " token pop /depth exch def pop",
1197 " /grays columns 8 add depth sub depth mul 8 idiv string def",
1198 " columns rows depth",
1203 " { currentfile grays readhexstring pop } image",
1208 " % colors: number of colors in the colormap.",
1209 " % colormap: red, green, blue color packets.",
1211 " currentfile buffer readline pop",
1212 " token pop /colors exch def pop",
1213 " /colors colors 3 mul def",
1214 " /colormap colors string def",
1215 " currentfile colormap readhexstring pop pop",
1216 " systemdict /colorimage known",
1223 " { PseudoClassPacket } false 3 colorimage",
1227 " % No colorimage operator; convert to grayscale.",
1234 " { GrayPseudoClassPacket } image",
1242 " % Display a DirectClass or PseudoClass image.",
1245 " % x & y translation.",
1247 " % label pointsize.",
1249 " % image columns & rows.",
1250 " % class: 0-DirectClass or 1-PseudoClass.",
1251 " % compression: 0-none or 1-RunlengthEncoded.",
1252 " % hex color packets.",
1255 " /buffer 512 string def",
1256 " /byte 1 string def",
1257 " /color_packet 3 string def",
1258 " /pixels 768 string def",
1260 " currentfile buffer readline pop",
1261 " token pop /x exch def",
1262 " token pop /y exch def pop",
1264 " currentfile buffer readline pop",
1265 " token pop /x exch def",
1266 " token pop /y exch def pop",
1267 " currentfile buffer readline pop",
1268 " token pop /pointsize exch def pop",
1269 " /Times-Roman findfont pointsize scalefont setfont",
1272 *PostscriptEpilog[]=
1275 " currentfile buffer readline pop",
1276 " token pop /columns exch def",
1277 " token pop /rows exch def pop",
1278 " currentfile buffer readline pop",
1279 " token pop /class exch def pop",
1280 " currentfile buffer readline pop",
1281 " token pop /compression exch def pop",
1282 " class 0 gt { PseudoClassImage } { DirectClassImage } ifelse",
1287 buffer[MaxTextExtent],
1288 date[MaxTextExtent],
1290 page_geometry[MaxTextExtent];
1334 register const IndexPacket
1337 register const PixelPacket
1344 register unsigned char
1366 Open output image file.
1368 assert(image_info != (const ImageInfo *) NULL);
1369 assert(image_info->signature == MagickSignature);
1370 assert(image != (Image *) NULL);
1371 assert(image->signature == MagickSignature);
1372 if (image->debug != MagickFalse)
1373 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1374 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1375 if (status == MagickFalse)
1377 (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
1383 Scale relative to dots-per-inch.
1385 if ((image->colorspace != RGBColorspace) &&
1386 (image->colorspace != CMYKColorspace))
1387 (void) TransformImageColorspace(image,RGBColorspace);
1388 delta.x=DefaultResolution;
1389 delta.y=DefaultResolution;
1390 resolution.x=image->x_resolution;
1391 resolution.y=image->y_resolution;
1392 if ((resolution.x == 0.0) || (resolution.y == 0.0))
1394 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1395 resolution.x=geometry_info.rho;
1396 resolution.y=geometry_info.sigma;
1397 if ((flags & SigmaValue) == 0)
1398 resolution.y=resolution.x;
1400 if (image_info->density != (char *) NULL)
1402 flags=ParseGeometry(image_info->density,&geometry_info);
1403 resolution.x=geometry_info.rho;
1404 resolution.y=geometry_info.sigma;
1405 if ((flags & SigmaValue) == 0)
1406 resolution.y=resolution.x;
1408 if (image->units == PixelsPerCentimeterResolution)
1413 SetGeometry(image,&geometry);
1414 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu",
1415 image->columns,image->rows);
1416 if (image_info->page != (char *) NULL)
1417 (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1419 if ((image->page.width != 0) && (image->page.height != 0))
1420 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu%+ld%+ld",
1421 image->page.width,image->page.height,image->page.x,image->page.y);
1423 if ((image->gravity != UndefinedGravity) &&
1424 (LocaleCompare(image_info->magick,"PS") == 0))
1425 (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1426 (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1427 (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1428 &geometry.width,&geometry.height);
1429 scale.x=(double) (geometry.width*delta.x)/resolution.x;
1430 geometry.width=(unsigned long) (scale.x+0.5);
1431 scale.y=(double) (geometry.height*delta.y)/resolution.y;
1432 geometry.height=(unsigned long) (scale.y+0.5);
1433 (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1434 (void) ParseGravityGeometry(image,page_geometry,&page_info,
1436 if (image->gravity != UndefinedGravity)
1438 geometry.x=(-page_info.x);
1439 geometry.y=(long) (media_info.height+page_info.y-image->rows);
1442 if (image_info->pointsize != 0.0)
1443 pointsize=image_info->pointsize;
1445 value=GetImageProperty(image,"label");
1446 if (value != (const char *) NULL)
1447 text_size=(unsigned long) (MultilineCensus(value)*pointsize+12);
1451 Output Postscript header.
1453 if (LocaleCompare(image_info->magick,"PS") == 0)
1454 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0\n",MaxTextExtent);
1456 (void) CopyMagickString(buffer,"%!PS-Adobe-3.0 EPSF-3.0\n",
1458 (void) WriteBlobString(image,buffer);
1459 (void) WriteBlobString(image,"%%Creator: (ImageMagick)\n");
1460 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Title: (%s)\n",
1462 (void) WriteBlobString(image,buffer);
1463 timer=time((time_t *) NULL);
1464 (void) FormatMagickTime(timer,MaxTextExtent,date);
1465 (void) FormatMagickString(buffer,MaxTextExtent,
1466 "%%%%CreationDate: (%s)\n",date);
1467 (void) WriteBlobString(image,buffer);
1468 bounds.x1=(double) geometry.x;
1469 bounds.y1=(double) geometry.y;
1470 bounds.x2=(double) geometry.x+scale.x;
1471 bounds.y2=(double) geometry.y+(geometry.height+text_size);
1472 if ((image_info->adjoin != MagickFalse) &&
1473 (GetNextImageInList(image) != (Image *) NULL))
1474 (void) CopyMagickString(buffer,"%%%%BoundingBox: (atend)\n",
1478 (void) FormatMagickString(buffer,MaxTextExtent,
1479 "%%%%BoundingBox: %ld %ld %ld %ld\n",(long) (bounds.x1+0.5),
1480 (long) (bounds.y1+0.5),(long) (bounds.x2+0.5),
1481 (long) (bounds.y2+0.5));
1482 (void) WriteBlobString(image,buffer);
1483 (void) FormatMagickString(buffer,MaxTextExtent,
1484 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,
1485 bounds.y1,bounds.x2,bounds.y2);
1487 (void) WriteBlobString(image,buffer);
1488 profile=GetImageProfile(image,"8bim");
1489 if (profile != (StringInfo *) NULL)
1492 Embed Photoshop profile.
1494 (void) FormatMagickString(buffer,MaxTextExtent,
1495 "%%BeginPhotoshop: %lu",(unsigned long) GetStringInfoLength(
1497 (void) WriteBlobString(image,buffer);
1498 for (i=0; i < (long) GetStringInfoLength(profile); i++)
1501 (void) WriteBlobString(image,"\n% ");
1502 (void) FormatMagickString(buffer,MaxTextExtent,"%02X",
1503 (unsigned int) (GetStringInfoDatum(profile)[i] & 0xff));
1504 (void) WriteBlobString(image,buffer);
1506 (void) WriteBlobString(image,"\n%EndPhotoshop\n");
1508 profile=GetImageProfile(image,"xmp");
1509 if (0 && (profile != (StringInfo *) NULL))
1514 (void) WriteBlobString(image,"\n%begin_xml_code\n");
1515 (void) FormatMagickString(buffer,MaxTextExtent,
1516 "\n%%begin_xml_packet: %lu\n",(unsigned long)
1517 GetStringInfoLength(profile));
1518 (void) WriteBlobString(image,buffer);
1519 for (i=0; i < (long) GetStringInfoLength(profile); i++)
1520 (void) WriteBlobByte(image,GetStringInfoDatum(profile)[i]);
1521 (void) WriteBlobString(image,"\n%end_xml_packet\n%end_xml_code\n");
1523 value=GetImageProperty(image,"label");
1524 if (value != (const char *) NULL)
1525 (void) WriteBlobString(image,
1526 "%%DocumentNeededResources: font Times-Roman\n");
1527 (void) WriteBlobString(image,"%%DocumentData: Clean7Bit\n");
1528 (void) WriteBlobString(image,"%%LanguageLevel: 1\n");
1529 if (LocaleCompare(image_info->magick,"PS") != 0)
1530 (void) WriteBlobString(image,"%%Pages: 1\n");
1534 Compute the number of pages.
1536 (void) WriteBlobString(image,"%%Orientation: Portrait\n");
1537 (void) WriteBlobString(image,"%%PageOrder: Ascend\n");
1538 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Pages: %lu\n",
1539 image_info->adjoin != MagickFalse ? (unsigned long)
1540 GetImageListLength(image) : 1UL);
1541 (void) WriteBlobString(image,buffer);
1543 (void) WriteBlobString(image,"%%EndComments\n");
1544 (void) WriteBlobString(image,"\n%%BeginDefaults\n");
1545 (void) WriteBlobString(image,"%%EndDefaults\n\n");
1546 if ((LocaleCompare(image_info->magick,"EPI") == 0) ||
1547 (LocaleCompare(image_info->magick,"EPSI") == 0) ||
1548 (LocaleCompare(image_info->magick,"EPT") == 0))
1563 Create preview image.
1565 preview_image=CloneImage(image,0,0,MagickTrue,&image->exception);
1566 if (preview_image == (Image *) NULL)
1567 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1569 Dump image as bitmap.
1571 (void) FormatMagickString(buffer,MaxTextExtent,
1572 "%%%%BeginPreview: %lu %lu %lu %lu\n%% ",preview_image->columns,
1573 preview_image->rows,1L,(((preview_image->columns+7) >> 3)*
1574 preview_image->rows+35)/36);
1575 (void) WriteBlobString(image,buffer);
1577 for (y=0; y < (long) image->rows; y++)
1579 p=GetVirtualPixels(preview_image,0,y,preview_image->columns,1,
1580 &preview_image->exception);
1581 if (p == (const PixelPacket *) NULL)
1583 indexes=GetVirtualIndexQueue(preview_image);
1586 for (x=0; x < (long) preview_image->columns; x++)
1589 pixel=PixelIntensityToQuantum(p);
1590 if (pixel >= (Quantum) (QuantumRange/2))
1595 q=PopHexPixel(hex_digits,byte,q);
1596 if ((q-pixels+8) >= 80)
1599 (void) WriteBlob(image,q-pixels,pixels);
1601 (void) WriteBlobString(image,"% ");
1610 q=PopHexPixel(hex_digits,byte,q);
1611 if ((q-pixels+8) >= 80)
1614 (void) WriteBlob(image,q-pixels,pixels);
1616 (void) WriteBlobString(image,"% ");
1623 (void) WriteBlob(image,q-pixels,pixels);
1625 (void) WriteBlobString(image,"\n%%EndPreview\n");
1626 preview_image=DestroyImage(preview_image);
1629 Output Postscript commands.
1631 for (s=PostscriptProlog; *s != (char *) NULL; s++)
1633 (void) FormatMagickString(buffer,MaxTextExtent,"%s\n",*s);
1634 (void) WriteBlobString(image,buffer);
1636 value=GetImageProperty(image,"label");
1637 if (value != (const char *) NULL)
1638 for (j=(long) MultilineCensus(value)-1; j >= 0; j--)
1640 (void) WriteBlobString(image," /label 512 string def\n");
1641 (void) WriteBlobString(image," currentfile label readline pop\n");
1642 (void) FormatMagickString(buffer,MaxTextExtent,
1643 " 0 y %g add moveto label show pop\n",j*pointsize+12);
1644 (void) WriteBlobString(image,buffer);
1646 for (s=PostscriptEpilog; *s != (char *) NULL; s++)
1648 (void) FormatMagickString(buffer,MaxTextExtent,"%s\n",*s);
1649 (void) WriteBlobString(image,buffer);
1651 if (LocaleCompare(image_info->magick,"PS") == 0)
1652 (void) WriteBlobString(image," showpage\n");
1653 (void) WriteBlobString(image,"} bind def\n");
1654 (void) WriteBlobString(image,"%%EndProlog\n");
1656 (void) FormatMagickString(buffer,MaxTextExtent,"%%%%Page: 1 %lu\n",page++);
1657 (void) WriteBlobString(image,buffer);
1658 (void) FormatMagickString(buffer,MaxTextExtent,
1659 "%%%%PageBoundingBox: %ld %ld %ld %ld\n",geometry.x,geometry.y,
1660 geometry.x+(long) geometry.width,geometry.y+(long) (geometry.height+
1662 (void) WriteBlobString(image,buffer);
1663 if ((double) geometry.x < bounds.x1)
1664 bounds.x1=(double) geometry.x;
1665 if ((double) geometry.y < bounds.y1)
1666 bounds.y1=(double) geometry.y;
1667 if ((double) (geometry.x+geometry.width-1) > bounds.x2)
1668 bounds.x2=(double) geometry.x+geometry.width-1;
1669 if ((double) (geometry.y+(geometry.height+text_size)-1) > bounds.y2)
1670 bounds.y2=(double) geometry.y+(geometry.height+text_size)-1;
1671 value=GetImageProperty(image,"label");
1672 if (value != (const char *) NULL)
1673 (void) WriteBlobString(image,"%%%%PageResources: font Times-Roman\n");
1674 if (LocaleCompare(image_info->magick,"PS") != 0)
1675 (void) WriteBlobString(image,"userdict begin\n");
1676 (void) WriteBlobString(image,"DisplayImage\n");
1680 (void) FormatMagickString(buffer,MaxTextExtent,
1681 "%ld %ld\n%g %g\n%g\n",geometry.x,geometry.y,scale.x,scale.y,
1683 (void) WriteBlobString(image,buffer);
1684 labels=(char **) NULL;
1685 value=GetImageProperty(image,"label");
1686 if (value != (const char *) NULL)
1687 labels=StringToList(value);
1688 if (labels != (char **) NULL)
1690 for (i=0; labels[i] != (char *) NULL; i++)
1692 (void) FormatMagickString(buffer,MaxTextExtent,"%s \n",
1694 (void) WriteBlobString(image,buffer);
1695 labels[i]=DestroyString(labels[i]);
1697 labels=(char **) RelinquishMagickMemory(labels);
1699 (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
1700 pixel.opacity=(Quantum) TransparentOpacity;
1701 index=(IndexPacket) 0;
1703 if ((image_info->type != TrueColorType) &&
1704 (IsGrayImage(image,&image->exception) != MagickFalse))
1706 if (IsMonochromeImage(image,&image->exception) == MagickFalse)
1712 Dump image as grayscale.
1714 (void) FormatMagickString(buffer,MaxTextExtent,
1715 "%lu %lu\n1\n1\n1\n8\n",image->columns,image->rows);
1716 (void) WriteBlobString(image,buffer);
1718 for (y=0; y < (long) image->rows; y++)
1720 p=GetVirtualPixels(image,0,y,image->columns,1,
1722 if (p == (const PixelPacket *) NULL)
1724 for (x=0; x < (long) image->columns; x++)
1726 pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
1727 q=PopHexPixel(hex_digits,pixel,q);
1729 if ((q-pixels+8) >= 80)
1732 (void) WriteBlob(image,q-pixels,pixels);
1737 if (image->previous == (Image *) NULL)
1739 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1740 if (status == MagickFalse)
1747 (void) WriteBlob(image,q-pixels,pixels);
1759 Dump image as bitmap.
1761 (void) FormatMagickString(buffer,MaxTextExtent,
1762 "%lu %lu\n1\n1\n1\n1\n",image->columns,image->rows);
1763 (void) WriteBlobString(image,buffer);
1765 for (y=0; y < (long) image->rows; y++)
1767 p=GetVirtualPixels(image,0,y,image->columns,1,
1769 if (p == (const PixelPacket *) NULL)
1771 indexes=GetVirtualIndexQueue(image);
1774 for (x=0; x < (long) image->columns; x++)
1777 pixel=PixelIntensityToQuantum(p);
1778 if (pixel >= (Quantum) (QuantumRange/2))
1783 q=PopHexPixel(hex_digits,byte,q);
1784 if ((q-pixels+2) >= 80)
1787 (void) WriteBlob(image,q-pixels,pixels);
1798 q=PopHexPixel(hex_digits,byte,q);
1799 if ((q-pixels+2) >= 80)
1802 (void) WriteBlob(image,q-pixels,pixels);
1806 if (image->previous == (Image *) NULL)
1808 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1809 if (status == MagickFalse)
1816 (void) WriteBlob(image,q-pixels,pixels);
1821 if ((image->storage_class == DirectClass) ||
1822 (image->colors > 256) || (image->matte != MagickFalse))
1825 Dump DirectClass image.
1827 (void) FormatMagickString(buffer,MaxTextExtent,"%lu %lu\n0\n%d\n",
1828 image->columns,image->rows,
1829 image_info->compression == RLECompression ? 1 : 0);
1830 (void) WriteBlobString(image,buffer);
1831 switch (image_info->compression)
1833 case RLECompression:
1836 Dump runlength-encoded DirectColor packets.
1839 for (y=0; y < (long) image->rows; y++)
1841 p=GetVirtualPixels(image,0,y,image->columns,1,
1843 if (p == (const PixelPacket *) NULL)
1847 for (x=0; x < (long) image->columns; x++)
1849 if ((p->red == pixel.red) && (p->green == pixel.green) &&
1850 (p->blue == pixel.blue) &&
1851 (p->opacity == pixel.opacity) && (length < 255) &&
1852 (x < (long) (image->columns-1)))
1858 WriteRunlengthPacket(image,pixel,length,p);
1859 if ((q-pixels+10) >= 80)
1862 (void) WriteBlob(image,q-pixels,pixels);
1871 WriteRunlengthPacket(image,pixel,length,p);
1872 if ((q-pixels+10) >= 80)
1875 (void) WriteBlob(image,q-pixels,pixels);
1878 if (image->previous == (Image *) NULL)
1880 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1881 if (status == MagickFalse)
1888 (void) WriteBlob(image,q-pixels,pixels);
1896 Dump uncompressed DirectColor packets.
1899 for (y=0; y < (long) image->rows; y++)
1901 p=GetVirtualPixels(image,0,y,image->columns,1,
1903 if (p == (const PixelPacket *) NULL)
1905 for (x=0; x < (long) image->columns; x++)
1907 if ((image->matte != MagickFalse) &&
1908 (p->opacity == (Quantum) TransparentOpacity))
1910 q=PopHexPixel(hex_digits,0xff,q);
1911 q=PopHexPixel(hex_digits,0xff,q);
1912 q=PopHexPixel(hex_digits,0xff,q);
1916 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetRedPixelComponent(p)),q);
1917 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetGreenPixelComponent(p)),q);
1918 q=PopHexPixel(hex_digits,ScaleQuantumToChar(GetBluePixelComponent(p)),q);
1920 if ((q-pixels+6) >= 80)
1923 (void) WriteBlob(image,q-pixels,pixels);
1928 if (image->previous == (Image *) NULL)
1930 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1931 if (status == MagickFalse)
1938 (void) WriteBlob(image,q-pixels,pixels);
1943 (void) WriteBlobByte(image,'\n');
1948 Dump PseudoClass image.
1950 (void) FormatMagickString(buffer,MaxTextExtent,"%lu %lu\n%d\n%d\n0\n",
1951 image->columns,image->rows,
1952 image->storage_class == PseudoClass ? 1 : 0,
1953 image_info->compression == RLECompression ? 1 : 0);
1954 (void) WriteBlobString(image,buffer);
1956 Dump number of colors and colormap.
1958 (void) FormatMagickString(buffer,MaxTextExtent,"%lu\n",image->colors);
1959 (void) WriteBlobString(image,buffer);
1960 for (i=0; i < (long) image->colors; i++)
1962 (void) FormatMagickString(buffer,MaxTextExtent,"%02X%02X%02X\n",
1963 ScaleQuantumToChar(image->colormap[i].red),
1964 ScaleQuantumToChar(image->colormap[i].green),
1965 ScaleQuantumToChar(image->colormap[i].blue));
1966 (void) WriteBlobString(image,buffer);
1968 switch (image_info->compression)
1970 case RLECompression:
1973 Dump runlength-encoded PseudoColor packets.
1976 for (y=0; y < (long) image->rows; y++)
1978 p=GetVirtualPixels(image,0,y,image->columns,1,
1980 if (p == (const PixelPacket *) NULL)
1982 indexes=GetVirtualIndexQueue(image);
1985 for (x=0; x < (long) image->columns; x++)
1987 if ((index == indexes[x]) && (length < 255) &&
1988 (x < ((long) image->columns-1)))
1994 q=PopHexPixel(hex_digits,index,q);
1995 q=PopHexPixel(hex_digits,(unsigned long)
1996 MagickMin(length,0xff),q);
1998 if ((q-pixels+6) >= 80)
2001 (void) WriteBlob(image,q-pixels,pixels);
2011 q=PopHexPixel(hex_digits,index,q);
2012 q=PopHexPixel(hex_digits,(unsigned long)
2013 MagickMin(length,0xff),q);
2014 if (image->previous == (Image *) NULL)
2016 status=SetImageProgress(image,SaveImageTag,y,image->rows);
2017 if (status == MagickFalse)
2024 (void) WriteBlob(image,q-pixels,pixels);
2032 Dump uncompressed PseudoColor packets.
2035 for (y=0; y < (long) image->rows; y++)
2037 p=GetVirtualPixels(image,0,y,image->columns,1,
2039 if (p == (const PixelPacket *) NULL)
2041 indexes=GetVirtualIndexQueue(image);
2042 for (x=0; x < (long) image->columns; x++)
2044 q=PopHexPixel(hex_digits,indexes[x],q);
2045 if ((q-pixels+4) >= 80)
2048 (void) WriteBlob(image,q-pixels,pixels);
2053 if (image->previous == (Image *) NULL)
2055 status=SetImageProgress(image,SaveImageTag,y,image->rows);
2056 if (status == MagickFalse)
2063 (void) WriteBlob(image,q-pixels,pixels);
2068 (void) WriteBlobByte(image,'\n');
2070 if (LocaleCompare(image_info->magick,"PS") != 0)
2071 (void) WriteBlobString(image,"end\n");
2072 (void) WriteBlobString(image,"%%PageTrailer\n");
2073 if (GetNextImageInList(image) == (Image *) NULL)
2075 image=SyncNextImageInList(image);
2076 status=SetImageProgress(image,SaveImagesTag,scene++,
2077 GetImageListLength(image));
2078 if (status == MagickFalse)
2080 } while (image_info->adjoin != MagickFalse);
2081 (void) WriteBlobString(image,"%%Trailer\n");
2084 (void) FormatMagickString(buffer,MaxTextExtent,
2085 "%%%%BoundingBox: %ld %ld %ld %ld\n",(long) (bounds.x1+0.5),
2086 (long) (bounds.y1+0.5),(long) (bounds.x2+0.5),(long) (bounds.y2+0.5));
2087 (void) WriteBlobString(image,buffer);
2088 (void) FormatMagickString(buffer,MaxTextExtent,
2089 "%%%%HiResBoundingBox: %g %g %g %g\n",bounds.x1,bounds.y1,
2090 bounds.x2,bounds.y2);
2091 (void) WriteBlobString(image,buffer);
2093 (void) WriteBlobString(image,"%%EOF\n");
2094 (void) CloseBlob(image);