]> granicus.if.org Git - imagemagick/blob - coders/pdf.c
(no commit message)
[imagemagick] / coders / pdf.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   DDDD   FFFFF                              %
7 %                            P   P  D   D  F                                  %
8 %                            PPPP   D   D  FFF                                %
9 %                            P      D   D  F                                  %
10 %                            P      DDDD   F                                  %
11 %                                                                             %
12 %                                                                             %
13 %                   Read/Write Portable Document Format                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
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/compress.h"
50 #include "magick/constitute.h"
51 #include "magick/delegate.h"
52 #include "magick/delegate-private.h"
53 #include "magick/draw.h"
54 #include "magick/exception.h"
55 #include "magick/exception-private.h"
56 #include "magick/geometry.h"
57 #include "magick/image.h"
58 #include "magick/image-private.h"
59 #include "magick/list.h"
60 #include "magick/magick.h"
61 #include "magick/memory_.h"
62 #include "magick/monitor.h"
63 #include "magick/monitor-private.h"
64 #include "magick/option.h"
65 #include "magick/profile.h"
66 #include "magick/property.h"
67 #include "magick/quantum-private.h"
68 #include "magick/resource_.h"
69 #include "magick/resize.h"
70 #include "magick/static.h"
71 #include "magick/string_.h"
72 #include "magick/module.h"
73 #include "magick/transform.h"
74 #include "magick/utility.h"
75 #include "magick/module.h"
76 \f
77 /*
78   Define declarations.
79 */
80 #if defined(MAGICKCORE_TIFF_DELEGATE)
81 #define CCITTParam  "-1"
82 #else
83 #define CCITTParam  "0"
84 #endif
85 \f
86 /*
87   Forward declarations.
88 */
89 static MagickBooleanType
90   WritePDFImage(const ImageInfo *,Image *);
91 \f
92 /*
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 %                                                                             %
95 %                                                                             %
96 %                                                                             %
97 %   I n v o k e P D F D e l e g a t e                                         %
98 %                                                                             %
99 %                                                                             %
100 %                                                                             %
101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102 %
103 %  InvokePDFDelegate() executes the PDF interpreter with the specified command.
104 %
105 %  The format of the InvokePDFDelegate method is:
106 %
107 %      MagickBooleanType InvokePDFDelegate(const MagickBooleanType verbose,
108 %        const char *command,ExceptionInfo *exception)
109 %
110 %  A description of each parameter follows:
111 %
112 %    o verbose: A value other than zero displays the command prior to
113 %      executing it.
114 %
115 %    o command: the address of a character string containing the command to
116 %      execute.
117 %
118 %    o exception: return any errors or warnings in this structure.
119 %
120 */
121 static MagickBooleanType InvokePDFDelegate(const MagickBooleanType verbose,
122   const char *command,ExceptionInfo *exception)
123 {
124   int
125     status;
126
127 #if defined(MAGICKCORE_GS_DELEGATE) || defined(MAGICKCORE_WINDOWS_SUPPORT)
128   char
129     **argv;
130
131   const GhostInfo
132     *ghost_info;
133
134   gs_main_instance
135     *interpreter;
136
137   int
138     argc,
139     code;
140
141   register ssize_t
142     i;
143
144 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
145   ghost_info=NTGhostscriptDLLVectors();
146 #else
147   GhostInfo
148     ghost_info_struct;
149
150   ghost_info=(&ghost_info_struct);
151   (void) ResetMagickMemory(&ghost_info,0,sizeof(ghost_info));
152   ghost_info_struct.new_instance=(int (*)(gs_main_instance **,void *))
153     gsapi_new_instance;
154   ghost_info_struct.init_with_args=(int (*)(gs_main_instance *,int,char **))
155     gsapi_init_with_args;
156   ghost_info_struct.run_string=(int (*)(gs_main_instance *,const char *,int,
157     int *)) gsapi_run_string;
158   ghost_info_struct.delete_instance=(void (*)(gs_main_instance *))
159     gsapi_delete_instance;
160   ghost_info_struct.exit=(int (*)(gs_main_instance *)) gsapi_exit;
161 #endif
162   if (ghost_info == (GhostInfo *) NULL)
163     {
164       status=SystemCommand(MagickFalse,verbose,command,exception);
165       return(status == 0 ? MagickTrue : MagickFalse);
166     }
167   if (verbose != MagickFalse)
168     {
169       (void) fputs("[ghostscript library]",stdout);
170       (void) fputs(strchr(command,' '),stdout);
171     }
172   status=(ghost_info->new_instance)(&interpreter,(void *) NULL);
173   if (status < 0)
174     {
175       status=SystemCommand(MagickFalse,verbose,command,exception);
176       return(status == 0 ? MagickTrue : MagickFalse);
177     }
178   argv=StringToArgv(command,&argc);
179   status=(ghost_info->init_with_args)(interpreter,argc-1,argv+1);
180   if (status == 0)
181     status=(ghost_info->run_string)(interpreter,"systemdict /start get exec\n",
182       0,&code);
183   (ghost_info->exit)(interpreter);
184   (ghost_info->delete_instance)(interpreter);
185 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
186   NTGhostscriptUnLoadDLL();
187 #endif
188   for (i=0; i < (ssize_t) argc; i++)
189     argv[i]=DestroyString(argv[i]);
190   argv=(char **) RelinquishMagickMemory(argv);
191   if ((status != 0) && (status != -101))
192     {
193       char
194         *message;
195
196       message=GetExceptionMessage(errno);
197       (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
198         "`%s': %s",command,message);
199       message=DestroyString(message);
200       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
201         "Ghostscript returns status %d, exit code %d",status,code);
202       return(MagickFalse);
203     }
204   return(MagickTrue);
205 #else
206   status=SystemCommand(MagickFalse,verbose,command,exception);
207   return(status == 0 ? MagickTrue : MagickFalse);
208 #endif
209 }
210 \f
211 /*
212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213 %                                                                             %
214 %                                                                             %
215 %                                                                             %
216 %   I s P D F                                                                 %
217 %                                                                             %
218 %                                                                             %
219 %                                                                             %
220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221 %
222 %  IsPDF() returns MagickTrue if the image format type, identified by the
223 %  magick string, is PDF.
224 %
225 %  The format of the IsPDF method is:
226 %
227 %      MagickBooleanType IsPDF(const unsigned char *magick,const size_t offset)
228 %
229 %  A description of each parameter follows:
230 %
231 %    o magick: compare image format pattern against these bytes.
232 %
233 %    o offset: Specifies the offset of the magick string.
234 %
235 */
236 static MagickBooleanType IsPDF(const unsigned char *magick,const size_t offset)
237 {
238   if (offset < 5)
239     return(MagickFalse);
240   if (LocaleNCompare((const char *) magick,"%PDF-",5) == 0)
241     return(MagickTrue);
242   return(MagickFalse);
243 }
244 \f
245 /*
246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 %                                                                             %
248 %                                                                             %
249 %                                                                             %
250 %   R e a d P D F I m a g e                                                   %
251 %                                                                             %
252 %                                                                             %
253 %                                                                             %
254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255 %
256 %  ReadPDFImage() reads a Portable Document Format image file and
257 %  returns it.  It allocates the memory necessary for the new Image structure
258 %  and returns a pointer to the new image.
259 %
260 %  The format of the ReadPDFImage method is:
261 %
262 %      Image *ReadPDFImage(const ImageInfo *image_info,ExceptionInfo *exception)
263 %
264 %  A description of each parameter follows:
265 %
266 %    o image_info: the image info.
267 %
268 %    o exception: return any errors or warnings in this structure.
269 %
270 */
271
272 static MagickBooleanType IsPDFRendered(const char *path)
273 {
274   MagickBooleanType
275     status;
276
277   struct stat
278     attributes;
279
280   if ((path == (const char *) NULL) || (*path == '\0'))
281     return(MagickFalse);
282   status=GetPathAttributes(path,&attributes);
283   if ((status != MagickFalse) && S_ISREG(attributes.st_mode) &&
284       (attributes.st_size > 0))
285     return(MagickTrue);
286   return(MagickFalse);
287 }
288
289 static Image *ReadPDFImage(const ImageInfo *image_info,ExceptionInfo *exception)
290 {
291 #define CropBox  "CropBox"
292 #define DeviceCMYK  "DeviceCMYK"
293 #define MediaBox  "MediaBox"
294 #define RenderPostscriptText  "Rendering Postscript...  "
295 #define PDFRotate  "Rotate"
296 #define SpotColor  "Separation"
297 #define TrimBox  "TrimBox"
298 #define PDFVersion  "PDF-"
299
300   char
301     command[MaxTextExtent],
302     density[MaxTextExtent],
303     filename[MaxTextExtent],
304     geometry[MaxTextExtent],
305     options[MaxTextExtent],
306     input_filename[MaxTextExtent],
307     postscript_filename[MaxTextExtent];
308
309   const char
310     *option;
311
312   const DelegateInfo
313     *delegate_info;
314
315   double
316     angle;
317
318   GeometryInfo
319     geometry_info;
320
321   Image
322     *image,
323     *next,
324     *pdf_image;
325
326   ImageInfo
327     *read_info;
328
329   int
330     file;
331
332   MagickBooleanType
333     cmyk,
334     cropbox,
335     trimbox,
336     status;
337
338   MagickStatusType
339     flags;
340
341   PointInfo
342     delta;
343
344   RectangleInfo
345     bounding_box,
346     page;
347
348   register char
349     *p;
350
351   register int
352     c;
353
354   SegmentInfo
355     bounds,
356     hires_bounds;
357
358   ssize_t
359     count;
360
361   size_t
362     scene,
363     spotcolor;
364
365   assert(image_info != (const ImageInfo *) NULL);
366   assert(image_info->signature == MagickSignature);
367   if (image_info->debug != MagickFalse)
368     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
369       image_info->filename);
370   assert(exception != (ExceptionInfo *) NULL);
371   assert(exception->signature == MagickSignature);
372   /*
373     Open image file.
374   */
375   image=AcquireImage(image_info);
376   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
377   if (status == MagickFalse)
378     {
379       image=DestroyImageList(image);
380       return((Image *) NULL);
381     }
382   status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
383   if (status == MagickFalse)
384     {
385       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
386         image_info->filename);
387       image=DestroyImageList(image);
388       return((Image *) NULL);
389     }
390   /*
391     Set the page density.
392   */
393   delta.x=DefaultResolution;
394   delta.y=DefaultResolution;
395   if ((image->x_resolution == 0.0) || (image->y_resolution == 0.0))
396     {
397       flags=ParseGeometry(PSDensityGeometry,&geometry_info);
398       image->x_resolution=geometry_info.rho;
399       image->y_resolution=geometry_info.sigma;
400       if ((flags & SigmaValue) == 0)
401         image->y_resolution=image->x_resolution;
402     }
403   /*
404     Determine page geometry from the PDF media box.
405   */
406   cmyk=image_info->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
407   cropbox=MagickFalse;
408   option=GetImageOption(image_info,"pdf:use-cropbox");
409   if (option != (const char *) NULL)
410     cropbox=IsMagickTrue(option);
411   trimbox=MagickFalse;
412   option=GetImageOption(image_info,"pdf:use-trimbox");
413   if (option != (const char *) NULL)
414     trimbox=IsMagickTrue(option);
415   count=0;
416   spotcolor=0;
417   (void) ResetMagickMemory(&bounding_box,0,sizeof(bounding_box));
418   (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
419   (void) ResetMagickMemory(&hires_bounds,0,sizeof(hires_bounds));
420   (void) ResetMagickMemory(&page,0,sizeof(page));
421   (void) ResetMagickMemory(command,0,sizeof(command));
422   hires_bounds.x2=0.0;
423   hires_bounds.y2=0.0;
424   angle=0.0;
425   p=command;
426   for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
427   {
428     /*
429       Note PDF elements.
430     */
431     if (c == '\n')
432       c=' ';
433     *p++=(char) c;
434     if ((c != (int) '/') && (c != (int) '%') && 
435         ((size_t) (p-command) < (MaxTextExtent-1)))
436       continue;
437     *(--p)='\0';
438     p=command;
439     if (LocaleNCompare(PDFRotate,command,strlen(PDFRotate)) == 0)
440       count=(ssize_t) sscanf(command,"Rotate %lf",&angle);
441     /*
442       Is this a CMYK document?
443     */
444     if (LocaleNCompare(DeviceCMYK,command,strlen(DeviceCMYK)) == 0)
445       cmyk=MagickTrue;
446     if (LocaleNCompare(SpotColor,command,strlen(SpotColor)) == 0)
447       {
448         char
449           name[MaxTextExtent],
450           property[MaxTextExtent],
451           *value;
452
453         register ssize_t
454           i;
455
456         /*
457           Note spot names.
458         */
459         (void) FormatMagickString(property,MaxTextExtent,"pdf:SpotColor-%.20g",
460           (double) spotcolor++);
461         i=0;
462         for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
463         {
464           if ((isspace(c) != 0) || (c == '/') || ((i+1) == MaxTextExtent))
465             break;
466           name[i++]=(char) c;
467         }
468         name[i]='\0';
469         value=AcquireString(name);
470         (void) SubstituteString(&value,"#20"," ");
471         (void) SetImageProperty(image,property,value);
472         value=DestroyString(value);
473         continue;
474       }
475     if (LocaleNCompare(PDFVersion,command,strlen(PDFVersion)) == 0)
476       (void) SetImageProperty(image,"pdf:Version",command);
477     count=0;
478     if (cropbox != MagickFalse)
479       {
480         if (LocaleNCompare(CropBox,command,strlen(CropBox)) == 0)
481           {
482             /*
483               Note region defined by crop box.
484             */
485             count=(ssize_t) sscanf(command,"CropBox [%lf %lf %lf %lf",
486               &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
487             if (count != 4)
488               count=(ssize_t) sscanf(command,"CropBox[%lf %lf %lf %lf",
489                 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
490           }
491       }
492     else
493       if (trimbox != MagickFalse)
494         {
495           if (LocaleNCompare(TrimBox,command,strlen(TrimBox)) == 0)
496             {
497               /*
498                 Note region defined by trim box.
499               */
500               count=(ssize_t) sscanf(command,"TrimBox [%lf %lf %lf %lf",
501                 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
502               if (count != 4)
503                 count=(ssize_t) sscanf(command,"TrimBox[%lf %lf %lf %lf",
504                   &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
505             }
506         }
507       else
508         if (LocaleNCompare(MediaBox,command,strlen(MediaBox)) == 0)
509           {
510             /*
511               Note region defined by media box.
512             */
513             count=(ssize_t) sscanf(command,"MediaBox [%lf %lf %lf %lf",
514               &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
515             if (count != 4)
516               count=(ssize_t) sscanf(command,"MediaBox[%lf %lf %lf %lf",
517                 &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
518           }
519     if (count != 4)
520       continue;
521     if (((bounds.x2 > hires_bounds.x2) && (bounds.y2 > hires_bounds.y2)) ||
522         ((hires_bounds.x2 == 0.0) && (hires_bounds.y2 == 0.0)))
523       {
524         /*
525           Set PDF render geometry.
526         */
527         (void) FormatMagickString(geometry,MaxTextExtent,
528           "%gx%g%+.15g%+.15g",bounds.x2-bounds.x1,bounds.y2-bounds.y1,
529            bounds.x1,bounds.y1);
530         (void) SetImageProperty(image,"pdf:HiResBoundingBox",geometry);
531         page.width=(size_t) floor(bounds.x2-bounds.x1+0.5);
532         page.height=(size_t) floor(bounds.y2-bounds.y1+0.5);
533         hires_bounds=bounds;
534       }
535   }
536   (void) CloseBlob(image);
537   if ((fabs(angle) == 90.0) || (fabs(angle) == 270.0))
538     {
539       size_t
540         swap;
541
542       swap=page.width;
543       page.width=page.height;
544       page.height=swap;
545     }
546   if (image_info->colorspace == RGBColorspace)
547     cmyk=MagickFalse;
548   /*
549     Create Ghostscript control file.
550   */
551   file=AcquireUniqueFileResource(postscript_filename);
552   if (file == -1)
553     {
554       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
555         image_info->filename);
556       image=DestroyImage(image);
557       return((Image *) NULL);
558     }
559   count=write(file," ",1);
560   file=close(file)-1;
561   /*
562     Render Postscript with the Ghostscript delegate.
563   */
564   if ((image_info->ping != MagickFalse) ||
565       (image_info->monochrome != MagickFalse))
566     delegate_info=GetDelegateInfo("ps:mono",(char *) NULL,exception);
567   else
568      if (cmyk != MagickFalse)
569        delegate_info=GetDelegateInfo("ps:cmyk",(char *) NULL,exception);
570      else
571        if (LocaleCompare(image_info->magick,"AI") == 0)
572          delegate_info=GetDelegateInfo("ps:alpha",(char *) NULL,exception);
573        else
574          delegate_info=GetDelegateInfo("ps:color",(char *) NULL,exception);
575   if (delegate_info == (const DelegateInfo *) NULL)
576     {
577       (void) RelinquishUniqueFileResource(postscript_filename);
578       image=DestroyImage(image);
579       return((Image *) NULL);
580     }
581   *options='\0';
582   if (image_info->density != (char *) NULL)
583     {
584       flags=ParseGeometry(image_info->density,&geometry_info);
585       image->x_resolution=geometry_info.rho;
586       image->y_resolution=geometry_info.sigma;
587       if ((flags & SigmaValue) == 0)
588         image->y_resolution=image->x_resolution;
589     }
590   (void) FormatMagickString(density,MaxTextExtent,"%gx%g",
591     image->x_resolution,image->y_resolution);
592   if (image_info->page != (char *) NULL)
593     {
594       (void) ParseAbsoluteGeometry(image_info->page,&page);
595       page.width=(size_t) floor(page.width*image->x_resolution/delta.x+
596         0.5);
597       page.height=(size_t) floor(page.height*image->y_resolution/delta.y+
598         0.5);
599       (void) FormatMagickString(options,MaxTextExtent,"-g%.20gx%.20g ",
600         (double) page.width,(double) page.height);
601     }
602   if (cmyk != MagickFalse)
603     (void) ConcatenateMagickString(options,"-dUseCIEColor ",MaxTextExtent);
604   if (cropbox != MagickFalse)
605     (void) ConcatenateMagickString(options,"-dUseCropBox ",MaxTextExtent);
606   if (trimbox != MagickFalse)
607     (void) ConcatenateMagickString(options,"-dUseTrimBox ",MaxTextExtent);
608   read_info=CloneImageInfo(image_info);
609   *read_info->magick='\0';
610   if (read_info->number_scenes != 0)
611     {
612       char
613         pages[MaxTextExtent];
614
615       (void) FormatMagickString(pages,MaxTextExtent,"-dFirstPage=%.20g "
616         "-dLastPage=%.20g",(double) read_info->scene+1,(double)
617         (read_info->scene+read_info->number_scenes));
618       (void) ConcatenateMagickString(options,pages,MaxTextExtent);
619       read_info->number_scenes=0;
620       if (read_info->scenes != (char *) NULL)
621         *read_info->scenes='\0';
622     }
623   if (read_info->authenticate != (char *) NULL)
624     (void) FormatMagickString(options+strlen(options),MaxTextExtent,
625       " -sPDFPassword=%s",read_info->authenticate);
626   (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
627   (void) AcquireUniqueFilename(read_info->filename);
628   (void) FormatMagickString(command,MaxTextExtent,
629     GetDelegateCommands(delegate_info),
630     read_info->antialias != MagickFalse ? 4 : 1,
631     read_info->antialias != MagickFalse ? 4 : 1,density,options,
632     read_info->filename,postscript_filename,input_filename);
633   status=InvokePDFDelegate(read_info->verbose,command,exception);
634   pdf_image=(Image *) NULL;
635   if ((status != MagickFalse) &&
636       (IsPDFRendered(read_info->filename) != MagickFalse))
637     pdf_image=ReadImage(read_info,exception);
638   (void) RelinquishUniqueFileResource(postscript_filename);
639   (void) RelinquishUniqueFileResource(read_info->filename);
640   (void) RelinquishUniqueFileResource(input_filename);
641   read_info=DestroyImageInfo(read_info);
642   if (pdf_image == (Image *) NULL)
643     {
644       ThrowFileException(exception,DelegateError,"PostscriptDelegateFailed",
645         image_info->filename);
646       return((Image *) NULL);
647     }
648   if (LocaleCompare(pdf_image->magick,"BMP") == 0)
649     {
650       Image
651         *cmyk_image;
652
653       cmyk_image=ConsolidateCMYKImages(pdf_image,exception);
654       if (cmyk_image != (Image *) NULL)
655         {
656           pdf_image=DestroyImageList(pdf_image);
657           pdf_image=cmyk_image;
658         }
659     }
660   if (image_info->number_scenes != 0)
661     {
662       Image
663         *clone_image;
664
665       register ssize_t
666         i;
667
668       /*
669         Add place holder images to meet the subimage specification requirement.
670       */
671       for (i=0; i < (ssize_t) image_info->scene; i++)
672       {
673         clone_image=CloneImage(pdf_image,1,1,MagickTrue,exception);
674         if (clone_image != (Image *) NULL)
675           PrependImageToList(&pdf_image,clone_image);
676       }
677     }
678   do
679   {
680     (void) CopyMagickString(pdf_image->filename,filename,MaxTextExtent);
681     pdf_image->page=page;
682     (void) CloneImageProfiles(pdf_image,image);
683     (void) CloneImageProperties(pdf_image,image);
684     next=SyncNextImageInList(pdf_image);
685     if (next != (Image *) NULL)
686       pdf_image=next;
687   } while (next != (Image *) NULL);
688   image=DestroyImage(image);
689   scene=0;
690   for (next=GetFirstImageInList(pdf_image); next != (Image *) NULL; )
691   {
692     next->scene=scene++;
693     next=GetNextImageInList(next);
694   }
695   return(GetFirstImageInList(pdf_image));
696 }
697 \f
698 /*
699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700 %                                                                             %
701 %                                                                             %
702 %                                                                             %
703 %   R e g i s t e r P D F I m a g e                                           %
704 %                                                                             %
705 %                                                                             %
706 %                                                                             %
707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
708 %
709 %  RegisterPDFImage() adds properties for the PDF image format to
710 %  the list of supported formats.  The properties include the image format
711 %  tag, a method to read and/or write the format, whether the format
712 %  supports the saving of more than one frame to the same file or blob,
713 %  whether the format supports native in-memory I/O, and a brief
714 %  description of the format.
715 %
716 %  The format of the RegisterPDFImage method is:
717 %
718 %      size_t RegisterPDFImage(void)
719 %
720 */
721 ModuleExport size_t RegisterPDFImage(void)
722 {
723   MagickInfo
724     *entry;
725
726   entry=SetMagickInfo("AI");
727   entry->decoder=(DecodeImageHandler *) ReadPDFImage;
728   entry->encoder=(EncodeImageHandler *) WritePDFImage;
729   entry->adjoin=MagickFalse;
730   entry->blob_support=MagickFalse;
731   entry->seekable_stream=MagickTrue;
732   entry->thread_support=EncoderThreadSupport;
733   entry->description=ConstantString("Adobe Illustrator CS2");
734   entry->module=ConstantString("PDF");
735   (void) RegisterMagickInfo(entry);
736   entry=SetMagickInfo("EPDF");
737   entry->decoder=(DecodeImageHandler *) ReadPDFImage;
738   entry->encoder=(EncodeImageHandler *) WritePDFImage;
739   entry->adjoin=MagickFalse;
740   entry->blob_support=MagickFalse;
741   entry->seekable_stream=MagickTrue;
742   entry->thread_support=EncoderThreadSupport;
743   entry->description=ConstantString("Encapsulated Portable Document Format");
744   entry->module=ConstantString("PDF");
745   (void) RegisterMagickInfo(entry);
746   entry=SetMagickInfo("PDF");
747   entry->decoder=(DecodeImageHandler *) ReadPDFImage;
748   entry->encoder=(EncodeImageHandler *) WritePDFImage;
749   entry->magick=(IsImageFormatHandler *) IsPDF;
750   entry->blob_support=MagickFalse;
751   entry->seekable_stream=MagickTrue;
752   entry->thread_support=EncoderThreadSupport;
753   entry->description=ConstantString("Portable Document Format");
754   entry->module=ConstantString("PDF");
755   (void) RegisterMagickInfo(entry);
756   entry=SetMagickInfo("PDFA");
757   entry->decoder=(DecodeImageHandler *) ReadPDFImage;
758   entry->encoder=(EncodeImageHandler *) WritePDFImage;
759   entry->magick=(IsImageFormatHandler *) IsPDF;
760   entry->blob_support=MagickFalse;
761   entry->seekable_stream=MagickTrue;
762   entry->thread_support=EncoderThreadSupport;
763   entry->description=ConstantString("Portable Document Archive Format");
764   entry->module=ConstantString("PDF");
765   (void) RegisterMagickInfo(entry);
766   return(MagickImageCoderSignature);
767 }
768 \f
769 /*
770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771 %                                                                             %
772 %                                                                             %
773 %                                                                             %
774 %   U n r e g i s t e r P D F I m a g e                                       %
775 %                                                                             %
776 %                                                                             %
777 %                                                                             %
778 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779 %
780 %  UnregisterPDFImage() removes format registrations made by the
781 %  PDF module from the list of supported formats.
782 %
783 %  The format of the UnregisterPDFImage method is:
784 %
785 %      UnregisterPDFImage(void)
786 %
787 */
788 ModuleExport void UnregisterPDFImage(void)
789 {
790   (void) UnregisterMagickInfo("AI");
791   (void) UnregisterMagickInfo("EPDF");
792   (void) UnregisterMagickInfo("PDF");
793   (void) UnregisterMagickInfo("PDFA");
794 }
795 \f
796 /*
797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798 %                                                                             %
799 %                                                                             %
800 %                                                                             %
801 %   W r i t e P D F I m a g e                                                 %
802 %                                                                             %
803 %                                                                             %
804 %                                                                             %
805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806 %
807 %  WritePDFImage() writes an image in the Portable Document image
808 %  format.
809 %
810 %  The format of the WritePDFImage method is:
811 %
812 %      MagickBooleanType WritePDFImage(const ImageInfo *image_info,Image *image)
813 %
814 %  A description of each parameter follows.
815 %
816 %    o image_info: the image info.
817 %
818 %    o image:  The image.
819 %
820 */
821
822 static inline size_t MagickMax(const size_t x,const size_t y)
823 {
824   if (x > y)
825     return(x);
826   return(y);
827 }
828
829 static inline size_t MagickMin(const size_t x,const size_t y)
830 {
831   if (x < y)
832     return(x);
833   return(y);
834 }
835
836 static char *EscapeParenthesis(const char *text)
837 {
838   register char
839     *p;
840
841   register ssize_t
842     i;
843
844   static char
845     buffer[MaxTextExtent];
846
847   size_t
848     escapes;
849
850   escapes=0;
851   p=buffer;
852   for (i=0; i < (ssize_t) MagickMin(strlen(text),(MaxTextExtent-escapes-1)); i++)
853   {
854     if ((text[i] == '(') || (text[i] == ')'))
855       {
856         *p++='\\';
857         escapes++;
858       }
859     *p++=text[i];
860   }
861   *p='\0';
862   return(buffer);
863 }
864
865 static MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
866   Image *image,Image *inject_image)
867 {
868   Image
869     *group4_image;
870
871   ImageInfo
872     *write_info;
873
874   MagickBooleanType
875     status;
876
877   size_t
878     length;
879
880   unsigned char
881     *group4;
882
883   status=MagickTrue;
884   write_info=CloneImageInfo(image_info);
885   (void) CopyMagickString(write_info->filename,"GROUP4:",MaxTextExtent);
886   (void) CopyMagickString(write_info->magick,"GROUP4",MaxTextExtent);
887   group4_image=CloneImage(inject_image,0,0,MagickTrue,&image->exception);
888   if (group4_image == (Image *) NULL)
889     return(MagickFalse);
890   group4=(unsigned char *) ImageToBlob(write_info,group4_image,&length,
891     &image->exception);
892   group4_image=DestroyImage(group4_image);
893   if (group4 == (unsigned char *) NULL)
894     return(MagickFalse);
895   write_info=DestroyImageInfo(write_info);
896   if (WriteBlob(image,length,group4) != (ssize_t) length)
897     status=MagickFalse;
898   group4=(unsigned char *) RelinquishMagickMemory(group4);
899   return(status);
900 }
901
902 static MagickBooleanType WritePDFImage(const ImageInfo *image_info,Image *image)
903 {
904 #define CFormat  "/Filter [ /%s ]\n"
905 #define ObjectsPerImage  14
906
907   static const char
908     XMPProfile[]=
909     {
910       "<?xpacket begin=\"%s\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n"
911       "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 4.0-c316 44.253921, Sun Oct 01 2006 17:08:23\">\n"
912       "   <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"
913       "      <rdf:Description rdf:about=\"\"\n"
914       "            xmlns:xap=\"http://ns.adobe.com/xap/1.0/\">\n"
915       "         <xap:ModifyDate>%s</xap:ModifyDate>\n"
916       "         <xap:CreateDate>%s</xap:CreateDate>\n"
917       "         <xap:MetadataDate>%s</xap:MetadataDate>\n"
918       "         <xap:CreatorTool>%s</xap:CreatorTool>\n"
919       "      </rdf:Description>\n"
920       "      <rdf:Description rdf:about=\"\"\n"
921       "            xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n"
922       "         <dc:format>application/pdf</dc:format>\n"
923       "      </rdf:Description>\n"
924       "      <rdf:Description rdf:about=\"\"\n"
925       "            xmlns:xapMM=\"http://ns.adobe.com/xap/1.0/mm/\">\n"
926       "         <xapMM:DocumentID>uuid:6ec119d7-7982-4f56-808d-dfe64f5b35cf</xapMM:DocumentID>\n"
927       "         <xapMM:InstanceID>uuid:a79b99b4-6235-447f-9f6c-ec18ef7555cb</xapMM:InstanceID>\n"
928       "      </rdf:Description>\n"
929       "      <rdf:Description rdf:about=\"\"\n"
930       "            xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n"
931       "         <pdf:Producer>%s</pdf:Producer>\n"
932       "      </rdf:Description>\n"
933       "      <rdf:Description rdf:about=\"\"\n"
934       "            xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n"
935       "         <pdfaid:part>1</pdfaid:part>\n"
936       "         <pdfaid:conformance>B</pdfaid:conformance>\n"
937       "      </rdf:Description>\n"
938       "   </rdf:RDF>\n"
939       "</x:xmpmeta>\n"
940       "<?xpacket end=\"w\"?>\n"
941     },
942     XMPProfileMagick[4]= { (char) 0xef, (char) 0xbb, (char) 0xbf, (char) 0x00 };
943
944   char
945     buffer[MaxTextExtent],
946     date[MaxTextExtent],
947     **labels,
948     page_geometry[MaxTextExtent];
949
950   CompressionType
951     compression;
952
953   const char
954     *value;
955
956   double
957     pointsize;
958
959   GeometryInfo
960     geometry_info;
961
962   ssize_t
963     count,
964     y;
965
966   Image
967     *next,
968     *tile_image;
969
970   MagickBooleanType
971     status;
972
973   MagickOffsetType
974     offset,
975     scene,
976     *xref;
977
978   MagickSizeType
979     number_pixels;
980
981   MagickStatusType
982     flags;
983
984   PointInfo
985     delta,
986     resolution,
987     scale;
988
989   RectangleInfo
990     geometry,
991     media_info,
992     page_info;
993
994   register const IndexPacket
995     *indexes;
996
997   register const PixelPacket
998     *p;
999
1000   register unsigned char
1001     *q;
1002
1003   register ssize_t
1004     i,
1005     x;
1006
1007   size_t
1008     length;
1009
1010   struct tm
1011     local_time;
1012
1013   time_t
1014     seconds;
1015
1016   unsigned char
1017     *pixels;
1018
1019   size_t
1020     info_id,
1021     object,
1022     pages_id,
1023     root_id,
1024     text_size,
1025     version;
1026
1027   /*
1028     Open output image file.
1029   */
1030   assert(image_info != (const ImageInfo *) NULL);
1031   assert(image_info->signature == MagickSignature);
1032   assert(image != (Image *) NULL);
1033   assert(image->signature == MagickSignature);
1034   if (image->debug != MagickFalse)
1035     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1036   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1037   if (status == MagickFalse)
1038     return(status);
1039   /*
1040     Allocate X ref memory.
1041   */
1042   xref=(MagickOffsetType *) AcquireQuantumMemory(2048UL,sizeof(*xref));
1043   if (xref == (MagickOffsetType *) NULL)
1044     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1045   (void) ResetMagickMemory(xref,0,2048UL*sizeof(*xref));
1046   /*
1047     Write Info object.
1048   */
1049   object=0;
1050   version=3;
1051   if (image_info->compression == JPEG2000Compression)
1052     version=(size_t) MagickMax(version,5);
1053   for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
1054     if (next->matte != MagickFalse)
1055       version=(size_t) MagickMax(version,4);
1056   if (LocaleCompare(image_info->magick,"PDFA") == 0)
1057     version=(size_t) MagickMax(version,6);
1058   (void) FormatMagickString(buffer,MaxTextExtent,"%%PDF-1.%.20g \n",
1059     (double) version);
1060   (void) WriteBlobString(image,buffer);
1061   if (LocaleCompare(image_info->magick,"PDFA") == 0)
1062     (void) WriteBlobString(image,"%âãÏÓ\n");
1063   /*
1064     Write Catalog object.
1065   */
1066   xref[object++]=TellBlob(image);
1067   root_id=object;
1068   (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1069     object);
1070   (void) WriteBlobString(image,buffer);
1071   (void) WriteBlobString(image,"<<\n");
1072   if (LocaleCompare(image_info->magick,"PDFA") != 0)
1073     (void) FormatMagickString(buffer,MaxTextExtent,"/Pages %.20g 0 R\n",
1074       (double) object+1);
1075   else
1076     {
1077       (void) FormatMagickString(buffer,MaxTextExtent,"/Metadata %.20g 0 R\n",
1078         (double) object+1);
1079       (void) WriteBlobString(image,buffer);
1080       (void) FormatMagickString(buffer,MaxTextExtent,"/Pages %.20g 0 R\n",
1081         (double) object+2);
1082     }
1083   (void) WriteBlobString(image,buffer);
1084   (void) WriteBlobString(image,"/Type /Catalog\n");
1085   (void) WriteBlobString(image,">>\n");
1086   (void) WriteBlobString(image,"endobj\n");
1087   if (LocaleCompare(image_info->magick,"PDFA") == 0)
1088     {
1089       char
1090         create_date[MaxTextExtent],
1091         modify_date[MaxTextExtent],
1092         timestamp[MaxTextExtent],
1093         xmp_profile[MaxTextExtent];
1094
1095       size_t
1096         version;
1097
1098       /*
1099         Write XMP object.
1100       */
1101       xref[object++]=TellBlob(image);
1102       (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",
1103         (double) object);
1104       (void) WriteBlobString(image,buffer);
1105       (void) WriteBlobString(image,"<<\n");
1106       (void) WriteBlobString(image,"/Subtype /XML\n");
1107       *modify_date='\0';
1108       value=GetImageProperty(image,"date:modify");
1109       if (value != (const char *) NULL)
1110         (void) CopyMagickString(modify_date,value,MaxTextExtent);
1111       *create_date='\0';
1112       value=GetImageProperty(image,"date:create");
1113       if (value != (const char *) NULL)
1114         (void) CopyMagickString(create_date,value,MaxTextExtent);
1115       (void) FormatMagickTime(time((time_t *) NULL),MaxTextExtent,timestamp);
1116       i=FormatMagickString(xmp_profile,MaxTextExtent,XMPProfile,
1117         XMPProfileMagick,modify_date,create_date,timestamp,
1118         GetMagickVersion(&version),GetMagickVersion(&version));
1119       (void) FormatMagickString(buffer,MaxTextExtent,"/Length %.20g\n",
1120         (double) i);
1121       (void) WriteBlobString(image,buffer);
1122       (void) WriteBlobString(image,"/Type /Metadata\n");
1123       (void) WriteBlobString(image,">>\nstream\n");
1124       (void) WriteBlobString(image,xmp_profile);
1125       (void) WriteBlobString(image,"endstream\n");
1126       (void) WriteBlobString(image,"endobj\n");
1127     }
1128   /*
1129     Write Pages object.
1130   */
1131   xref[object++]=TellBlob(image);
1132   pages_id=object;
1133   (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1134     object);
1135   (void) WriteBlobString(image,buffer);
1136   (void) WriteBlobString(image,"<<\n");
1137   (void) WriteBlobString(image,"/Type /Pages\n");
1138   (void) FormatMagickString(buffer,MaxTextExtent,"/Kids [ %.20g 0 R ",
1139     (double) object+1);
1140   (void) WriteBlobString(image,buffer);
1141   count=(ssize_t) (pages_id+ObjectsPerImage+1);
1142   if (image_info->adjoin != MagickFalse)
1143     {
1144       Image
1145         *kid_image;
1146
1147       /*
1148         Predict page object id's.
1149       */
1150       kid_image=image;
1151       for ( ; GetNextImageInList(kid_image) != (Image *) NULL; count+=ObjectsPerImage)
1152       {
1153         (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 R ",(double)
1154           count);
1155         (void) WriteBlobString(image,buffer);
1156         kid_image=GetNextImageInList(kid_image);
1157       }
1158       xref=(MagickOffsetType *) ResizeQuantumMemory(xref,(size_t) count+2048UL,
1159         sizeof(*xref));
1160       if (xref == (MagickOffsetType *) NULL)
1161         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1162     }
1163   (void) WriteBlobString(image,"]\n");
1164   (void) FormatMagickString(buffer,MaxTextExtent,"/Count %.20g\n",
1165     (double) ((count-pages_id)/ObjectsPerImage));
1166   (void) WriteBlobString(image,buffer);
1167   (void) WriteBlobString(image,">>\n");
1168   (void) WriteBlobString(image,"endobj\n");
1169   scene=0;
1170   do
1171   {
1172     compression=image->compression;
1173     if (image_info->compression != UndefinedCompression)
1174       compression=image_info->compression;
1175     switch (compression)
1176     {
1177       case FaxCompression:
1178       case Group4Compression:
1179       {
1180         if ((IsMonochromeImage(image,&image->exception) == MagickFalse) ||
1181             (image->matte != MagickFalse))
1182           compression=RLECompression;
1183         break;
1184       }
1185 #if !defined(MAGICKCORE_JPEG_DELEGATE)
1186       case JPEGCompression:
1187       {
1188         compression=RLECompression;
1189         (void) ThrowMagickException(&image->exception,GetMagickModule(),
1190           MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (JPEG)",
1191           image->filename);
1192         break;
1193       }
1194 #endif
1195 #if !defined(MAGICKCORE_JP2_DELEGATE)
1196       case JPEG2000Compression:
1197       {
1198         compression=RLECompression;
1199         (void) ThrowMagickException(&image->exception,GetMagickModule(),
1200           MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (JP2)",
1201           image->filename);
1202         break;
1203       }
1204 #endif
1205 #if !defined(MAGICKCORE_ZLIB_DELEGATE)
1206       case ZipCompression:
1207       {
1208         compression=RLECompression;
1209         (void) ThrowMagickException(&image->exception,GetMagickModule(),
1210           MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (ZLIB)",
1211           image->filename);
1212         break;
1213       }
1214 #endif
1215       case LZWCompression:
1216       {
1217         if (LocaleCompare(image_info->magick,"PDFA") == 0)
1218           compression=RLECompression;  /* LZW compression is forbidden */
1219         break;
1220       }
1221       case NoCompression:
1222       {
1223         if (LocaleCompare(image_info->magick,"PDFA") == 0)
1224           compression=RLECompression; /* ASCII 85 compression is forbidden */
1225         break;
1226       }
1227       default:
1228         break;
1229     }
1230     if (compression == JPEG2000Compression)
1231       {
1232         if (image->colorspace != RGBColorspace)
1233           (void) TransformImageColorspace(image,RGBColorspace);
1234       }
1235     /*
1236       Scale relative to dots-per-inch.
1237     */
1238     delta.x=DefaultResolution;
1239     delta.y=DefaultResolution;
1240     resolution.x=image->x_resolution;
1241     resolution.y=image->y_resolution;
1242     if ((resolution.x == 0.0) || (resolution.y == 0.0))
1243       {
1244         flags=ParseGeometry(PSDensityGeometry,&geometry_info);
1245         resolution.x=geometry_info.rho;
1246         resolution.y=geometry_info.sigma;
1247         if ((flags & SigmaValue) == 0)
1248           resolution.y=resolution.x;
1249       }
1250     if (image_info->density != (char *) NULL)
1251       {
1252         flags=ParseGeometry(image_info->density,&geometry_info);
1253         resolution.x=geometry_info.rho;
1254         resolution.y=geometry_info.sigma;
1255         if ((flags & SigmaValue) == 0)
1256           resolution.y=resolution.x;
1257       }
1258     if (image->units == PixelsPerCentimeterResolution)
1259       {
1260         resolution.x=(size_t) (100.0*2.54*resolution.x+0.5)/100.0;
1261         resolution.y=(size_t) (100.0*2.54*resolution.y+0.5)/100.0;
1262       }
1263     SetGeometry(image,&geometry);
1264     (void) FormatMagickString(page_geometry,MaxTextExtent,"%.20gx%.20g",
1265       (double) image->columns,(double) image->rows);
1266     if (image_info->page != (char *) NULL)
1267       (void) CopyMagickString(page_geometry,image_info->page,MaxTextExtent);
1268     else
1269       if ((image->page.width != 0) && (image->page.height != 0))
1270         (void) FormatMagickString(page_geometry,MaxTextExtent,
1271           "%.20gx%.20g%+.20g%+.20g",(double) image->page.width,
1272           (double) image->page.height,(double) image->page.x,(double)
1273           image->page.y);
1274       else
1275         if ((image->gravity != UndefinedGravity) &&
1276             (LocaleCompare(image_info->magick,"PDF") == 0))
1277           (void) CopyMagickString(page_geometry,PSPageGeometry,MaxTextExtent);
1278     (void) ConcatenateMagickString(page_geometry,">",MaxTextExtent);
1279     (void) ParseMetaGeometry(page_geometry,&geometry.x,&geometry.y,
1280       &geometry.width,&geometry.height);
1281     scale.x=(double) (geometry.width*delta.x)/resolution.x;
1282     geometry.width=(size_t) floor(scale.x+0.5);
1283     scale.y=(double) (geometry.height*delta.y)/resolution.y;
1284     geometry.height=(size_t) floor(scale.y+0.5);
1285     (void) ParseAbsoluteGeometry(page_geometry,&media_info);
1286     (void) ParseGravityGeometry(image,page_geometry,&page_info,
1287       &image->exception);
1288     if (image->gravity != UndefinedGravity)
1289       {
1290         geometry.x=(-page_info.x);
1291         geometry.y=(ssize_t) (media_info.height+page_info.y-image->rows);
1292       }
1293     pointsize=12.0;
1294     if (image_info->pointsize != 0.0)
1295       pointsize=image_info->pointsize;
1296     text_size=0;
1297     value=GetImageProperty(image,"Label");
1298     if (value != (const char *) NULL)
1299       text_size=(size_t) (MultilineCensus(value)*pointsize+12);
1300     /*
1301       Write Page object.
1302     */
1303     xref[object++]=TellBlob(image);
1304     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1305       object);
1306     (void) WriteBlobString(image,buffer);
1307     (void) WriteBlobString(image,"<<\n");
1308     (void) WriteBlobString(image,"/Type /Page\n");
1309     (void) FormatMagickString(buffer,MaxTextExtent,"/Parent %.20g 0 R\n",
1310       (double) pages_id);
1311     (void) WriteBlobString(image,buffer);
1312     (void) WriteBlobString(image,"/Resources <<\n");
1313     labels=(char **) NULL;
1314     value=GetImageProperty(image,"Label");
1315     if (value != (const char *) NULL)
1316       labels=StringToList(value);
1317     if (labels != (char **) NULL)
1318       {
1319         (void) FormatMagickString(buffer,MaxTextExtent,
1320           "/Font << /F%.20g %.20g 0 R >>\n",(double) image->scene,(double)
1321           object+4);
1322         (void) WriteBlobString(image,buffer);
1323       }
1324     (void) FormatMagickString(buffer,MaxTextExtent,
1325       "/XObject << /Im%.20g %.20g 0 R >>\n",(double) image->scene,(double)
1326       object+5);
1327     (void) WriteBlobString(image,buffer);
1328     (void) FormatMagickString(buffer,MaxTextExtent,"/ProcSet %.20g 0 R >>\n",
1329       (double) object+3);
1330     (void) WriteBlobString(image,buffer);
1331     (void) FormatMagickString(buffer,MaxTextExtent,
1332       "/MediaBox [0 0 %g %g]\n",72.0*media_info.width/resolution.x,
1333       72.0*media_info.height/resolution.y);
1334     (void) WriteBlobString(image,buffer);
1335     (void) FormatMagickString(buffer,MaxTextExtent,
1336       "/CropBox [0 0 %g %g]\n",72.0*media_info.width/resolution.x,
1337       72.0*media_info.height/resolution.y);
1338     (void) WriteBlobString(image,buffer);
1339     (void) FormatMagickString(buffer,MaxTextExtent,"/Contents %.20g 0 R\n",
1340       (double) object+1);
1341     (void) WriteBlobString(image,buffer);
1342     (void) FormatMagickString(buffer,MaxTextExtent,"/Thumb %.20g 0 R\n",
1343       (double) object+8);
1344     (void) WriteBlobString(image,buffer);
1345     (void) WriteBlobString(image,">>\n");
1346     (void) WriteBlobString(image,"endobj\n");
1347     /*
1348       Write Contents object.
1349     */
1350     xref[object++]=TellBlob(image);
1351     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1352       object);
1353     (void) WriteBlobString(image,buffer);
1354     (void) WriteBlobString(image,"<<\n");
1355     (void) FormatMagickString(buffer,MaxTextExtent,"/Length %.20g 0 R\n",
1356       (double) object+1);
1357     (void) WriteBlobString(image,buffer);
1358     (void) WriteBlobString(image,">>\n");
1359     (void) WriteBlobString(image,"stream\n");
1360     offset=TellBlob(image);
1361     (void) WriteBlobString(image,"q\n");
1362     if (labels != (char **) NULL)
1363       for (i=0; labels[i] != (char *) NULL; i++)
1364       {
1365         (void) WriteBlobString(image,"BT\n");
1366         (void) FormatMagickString(buffer,MaxTextExtent,"/F%.20g %g Tf\n",
1367           (double) image->scene,pointsize);
1368         (void) WriteBlobString(image,buffer);
1369         (void) FormatMagickString(buffer,MaxTextExtent,"%.20g %.20g Td\n",
1370           (double) geometry.x,(double) (geometry.y+geometry.height+i*pointsize+
1371           12));
1372         (void) WriteBlobString(image,buffer);
1373         (void) FormatMagickString(buffer,MaxTextExtent,"(%s) Tj\n",labels[i]);
1374         (void) WriteBlobString(image,buffer);
1375         (void) WriteBlobString(image,"ET\n");
1376         labels[i]=DestroyString(labels[i]);
1377       }
1378     (void) FormatMagickString(buffer,MaxTextExtent,"%g 0 0 %g %.20g %.20g cm\n",
1379       scale.x,scale.y,(double) geometry.x,(double) geometry.y);
1380     (void) WriteBlobString(image,buffer);
1381     (void) FormatMagickString(buffer,MaxTextExtent,"/Im%.20g Do\n",
1382       (double) image->scene);
1383     (void) WriteBlobString(image,buffer);
1384     (void) WriteBlobString(image,"Q\n");
1385     offset=TellBlob(image)-offset;
1386     (void) WriteBlobString(image,"endstream\n");
1387     (void) WriteBlobString(image,"endobj\n");
1388     /*
1389       Write Length object.
1390     */
1391     xref[object++]=TellBlob(image);
1392     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1393       object);
1394     (void) WriteBlobString(image,buffer);
1395     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",(double) offset);
1396     (void) WriteBlobString(image,buffer);
1397     (void) WriteBlobString(image,"endobj\n");
1398     /*
1399       Write Procset object.
1400     */
1401     xref[object++]=TellBlob(image);
1402     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",
1403       (double) object);
1404     (void) WriteBlobString(image,buffer);
1405     if ((image->storage_class == DirectClass) || (image->colors > 256))
1406       (void) CopyMagickString(buffer,"[ /PDF /Text /ImageC",MaxTextExtent);
1407     else
1408       if ((compression == FaxCompression) || (compression == Group4Compression))
1409         (void) CopyMagickString(buffer,"[ /PDF /Text /ImageB",MaxTextExtent);
1410       else
1411         (void) CopyMagickString(buffer,"[ /PDF /Text /ImageI",MaxTextExtent);
1412     (void) WriteBlobString(image,buffer);
1413     (void) WriteBlobString(image," ]\n");
1414     (void) WriteBlobString(image,"endobj\n");
1415     /*
1416       Write Font object.
1417     */
1418     xref[object++]=TellBlob(image);
1419     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",
1420       (double) object);
1421     (void) WriteBlobString(image,buffer);
1422     (void) WriteBlobString(image,"<<\n");
1423     if (labels != (char **) NULL)
1424       {
1425         (void) WriteBlobString(image,"/Type /Font\n");
1426         (void) WriteBlobString(image,"/Subtype /Type1\n");
1427         (void) FormatMagickString(buffer,MaxTextExtent,"/Name /F%.20g\n",
1428           (double) image->scene);
1429         (void) WriteBlobString(image,buffer);
1430         (void) WriteBlobString(image,"/BaseFont /Helvetica\n");
1431         (void) WriteBlobString(image,"/Encoding /MacRomanEncoding\n");
1432         labels=(char **) RelinquishMagickMemory(labels);
1433       }
1434     (void) WriteBlobString(image,">>\n");
1435     (void) WriteBlobString(image,"endobj\n");
1436     /*
1437       Write XObject object.
1438     */
1439     xref[object++]=TellBlob(image);
1440     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1441       object);
1442     (void) WriteBlobString(image,buffer);
1443     (void) WriteBlobString(image,"<<\n");
1444     (void) WriteBlobString(image,"/Type /XObject\n");
1445     (void) WriteBlobString(image,"/Subtype /Image\n");
1446     (void) FormatMagickString(buffer,MaxTextExtent,"/Name /Im%.20g\n",
1447       (double) image->scene);
1448     (void) WriteBlobString(image,buffer);
1449     switch (compression)
1450     {
1451       case NoCompression:
1452       {
1453         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"ASCII85Decode");
1454         break;
1455       }
1456       case JPEGCompression:
1457       {
1458         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"DCTDecode");
1459         if (image->colorspace != CMYKColorspace)
1460           break;
1461         (void) WriteBlobString(image,buffer);
1462         (void) CopyMagickString(buffer,"/Decode [1 0 1 0 1 0 1 0]\n",
1463           MaxTextExtent);
1464         break;
1465       }
1466       case JPEG2000Compression:
1467       {
1468         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"JPXDecode");
1469         if (image->colorspace != CMYKColorspace)
1470           break;
1471         (void) WriteBlobString(image,buffer);
1472         (void) CopyMagickString(buffer,"/Decode [1 0 1 0 1 0 1 0]\n",
1473           MaxTextExtent);
1474         break;
1475       }
1476       case LZWCompression:
1477       {
1478         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"LZWDecode");
1479         break;
1480       }
1481       case ZipCompression:
1482       {
1483         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"FlateDecode");
1484         break;
1485       }
1486       case FaxCompression:
1487       case Group4Compression:
1488       {
1489         (void) CopyMagickString(buffer,"/Filter [ /CCITTFaxDecode ]\n",
1490           MaxTextExtent);
1491         (void) WriteBlobString(image,buffer);
1492         (void) FormatMagickString(buffer,MaxTextExtent,"/DecodeParms [ << "
1493           "/K %s /BlackIs1 false /Columns %.20g /Rows %.20g >> ]\n",CCITTParam,
1494           (double) image->columns,(double) image->rows);
1495         break;
1496       }
1497       default:
1498       {
1499         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,
1500           "RunLengthDecode");
1501         break;
1502       }
1503     }
1504     (void) WriteBlobString(image,buffer);
1505     (void) FormatMagickString(buffer,MaxTextExtent,"/Width %.20g\n",(double)
1506       image->columns);
1507     (void) WriteBlobString(image,buffer);
1508     (void) FormatMagickString(buffer,MaxTextExtent,"/Height %.20g\n",(double)
1509       image->rows);
1510     (void) WriteBlobString(image,buffer);
1511     (void) FormatMagickString(buffer,MaxTextExtent,"/ColorSpace %.20g 0 R\n",
1512       (double) object+2);
1513     (void) WriteBlobString(image,buffer);
1514     (void) FormatMagickString(buffer,MaxTextExtent,"/BitsPerComponent %d\n",
1515       (compression == FaxCompression) || (compression == Group4Compression) ?
1516       1 : 8);
1517     (void) WriteBlobString(image,buffer);
1518     if (image->matte != MagickFalse)
1519       {
1520         (void) FormatMagickString(buffer,MaxTextExtent,"/SMask %.20g 0 R\n",
1521           (double) object+7);
1522         (void) WriteBlobString(image,buffer);
1523       }
1524     (void) FormatMagickString(buffer,MaxTextExtent,"/Length %.20g 0 R\n",
1525       (double) object+1);
1526     (void) WriteBlobString(image,buffer);
1527     (void) WriteBlobString(image,">>\n");
1528     (void) WriteBlobString(image,"stream\n");
1529     offset=TellBlob(image);
1530     number_pixels=(MagickSizeType) image->columns*image->rows;
1531     if ((4*number_pixels) != (MagickSizeType) ((size_t) (4*number_pixels)))
1532       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1533     if ((compression == FaxCompression) || (compression == Group4Compression) ||
1534         ((image_info->type != TrueColorType) &&
1535          (IsGrayImage(image,&image->exception) != MagickFalse)))
1536       {
1537         switch (compression)
1538         {
1539           case FaxCompression:
1540           case Group4Compression:
1541           {
1542             if (LocaleCompare(CCITTParam,"0") == 0)
1543               {
1544                 (void) HuffmanEncodeImage(image_info,image,image);
1545                 break;
1546               }
1547             (void) Huffman2DEncodeImage(image_info,image,image);
1548             break;
1549           }
1550           case JPEGCompression:
1551           {
1552             status=InjectImageBlob(image_info,image,image,"jpeg",
1553               &image->exception);
1554             if (status == MagickFalse)
1555               ThrowWriterException(CoderError,image->exception.reason);
1556             break;
1557           }
1558           case JPEG2000Compression:
1559           {
1560             status=InjectImageBlob(image_info,image,image,"jp2",
1561               &image->exception);
1562             if (status == MagickFalse)
1563               ThrowWriterException(CoderError,image->exception.reason);
1564             break;
1565           }
1566           case RLECompression:
1567           default:
1568           {
1569             /*
1570               Allocate pixel array.
1571             */
1572             length=(size_t) number_pixels;
1573             pixels=(unsigned char *) AcquireQuantumMemory(length,
1574               sizeof(*pixels));
1575             if (pixels == (unsigned char *) NULL)
1576               ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1577             /*
1578               Dump Runlength encoded pixels.
1579             */
1580             q=pixels;
1581             for (y=0; y < (ssize_t) image->rows; y++)
1582             {
1583               p=GetVirtualPixels(image,0,y,image->columns,1,
1584                 &image->exception);
1585               if (p == (const PixelPacket *) NULL)
1586                 break;
1587               for (x=0; x < (ssize_t) image->columns; x++)
1588               {
1589                 *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
1590                 p++;
1591               }
1592               if (image->previous == (Image *) NULL)
1593                 {
1594                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1595                 image->rows);
1596                   if (status == MagickFalse)
1597                     break;
1598                 }
1599             }
1600 #if defined(MAGICKCORE_ZLIB_DELEGATE)
1601             if (compression == ZipCompression)
1602               status=ZLIBEncodeImage(image,length,pixels);
1603             else
1604 #endif
1605               if (compression == LZWCompression)
1606                 status=LZWEncodeImage(image,length,pixels);
1607               else
1608                 status=PackbitsEncodeImage(image,length,pixels);
1609             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1610             if (status == MagickFalse)
1611               {
1612                 (void) CloseBlob(image);
1613                 return(MagickFalse);
1614               }
1615             break;
1616           }
1617           case NoCompression:
1618           {
1619             /*
1620               Dump uncompressed PseudoColor packets.
1621             */
1622             Ascii85Initialize(image);
1623             for (y=0; y < (ssize_t) image->rows; y++)
1624             {
1625               p=GetVirtualPixels(image,0,y,image->columns,1,
1626                 &image->exception);
1627               if (p == (const PixelPacket *) NULL)
1628                 break;
1629               for (x=0; x < (ssize_t) image->columns; x++)
1630               {
1631                 Ascii85Encode(image,
1632                   ScaleQuantumToChar(PixelIntensityToQuantum(p)));
1633                 p++;
1634               }
1635               if (image->previous == (Image *) NULL)
1636                 {
1637                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1638                 image->rows);
1639                   if (status == MagickFalse)
1640                     break;
1641                 }
1642             }
1643             Ascii85Flush(image);
1644             break;
1645           }
1646         }
1647       }
1648     else
1649       if ((image->storage_class == DirectClass) || (image->colors > 256) ||
1650           (compression == JPEGCompression) ||
1651           (compression == JPEG2000Compression))
1652         switch (compression)
1653         {
1654           case JPEGCompression:
1655           {
1656             status=InjectImageBlob(image_info,image,image,"jpeg",
1657               &image->exception);
1658             if (status == MagickFalse)
1659               ThrowWriterException(CoderError,image->exception.reason);
1660             break;
1661           }
1662           case JPEG2000Compression:
1663           {
1664             status=InjectImageBlob(image_info,image,image,"jp2",
1665               &image->exception);
1666             if (status == MagickFalse)
1667               ThrowWriterException(CoderError,image->exception.reason);
1668             break;
1669           }
1670           case RLECompression:
1671           default:
1672           {
1673             /*
1674               Allocate pixel array.
1675             */
1676             length=(size_t) number_pixels;
1677             pixels=(unsigned char *) AcquireQuantumMemory(length,
1678               4*sizeof(*pixels));
1679             length*=image->colorspace == CMYKColorspace ? 4UL : 3UL;
1680             if (pixels == (unsigned char *) NULL)
1681               ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1682             /*
1683               Dump runoffset encoded pixels.
1684             */
1685             q=pixels;
1686             for (y=0; y < (ssize_t) image->rows; y++)
1687             {
1688               p=GetVirtualPixels(image,0,y,image->columns,1,
1689                 &image->exception);
1690               if (p == (const PixelPacket *) NULL)
1691                 break;
1692               indexes=GetVirtualIndexQueue(image);
1693               for (x=0; x < (ssize_t) image->columns; x++)
1694               {
1695                 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
1696                 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
1697                 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
1698                 if (image->colorspace == CMYKColorspace)
1699                   *q++=ScaleQuantumToChar(indexes[x]);
1700                 p++;
1701               }
1702               if (image->previous == (Image *) NULL)
1703                 {
1704                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1705                 image->rows);
1706                   if (status == MagickFalse)
1707                     break;
1708                 }
1709             }
1710 #if defined(MAGICKCORE_ZLIB_DELEGATE)
1711             if (compression == ZipCompression)
1712               status=ZLIBEncodeImage(image,length,pixels);
1713             else
1714 #endif
1715               if (compression == LZWCompression)
1716                 status=LZWEncodeImage(image,length,pixels);
1717               else
1718                 status=PackbitsEncodeImage(image,length,pixels);
1719             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1720             if (status == MagickFalse)
1721               {
1722                 (void) CloseBlob(image);
1723                 return(MagickFalse);
1724               }
1725             break;
1726           }
1727           case NoCompression:
1728           {
1729             /*
1730               Dump uncompressed DirectColor packets.
1731             */
1732             Ascii85Initialize(image);
1733             for (y=0; y < (ssize_t) image->rows; y++)
1734             {
1735               p=GetVirtualPixels(image,0,y,image->columns,1,
1736                 &image->exception);
1737               if (p == (const PixelPacket *) NULL)
1738                 break;
1739               indexes=GetVirtualIndexQueue(image);
1740               for (x=0; x < (ssize_t) image->columns; x++)
1741               {
1742                 Ascii85Encode(image,ScaleQuantumToChar(GetRedPixelComponent(p)));
1743                 Ascii85Encode(image,ScaleQuantumToChar(GetGreenPixelComponent(p)));
1744                 Ascii85Encode(image,ScaleQuantumToChar(GetBluePixelComponent(p)));
1745                 if (image->colorspace == CMYKColorspace)
1746                   Ascii85Encode(image,ScaleQuantumToChar(indexes[x]));
1747                 p++;
1748               }
1749               if (image->previous == (Image *) NULL)
1750                 {
1751                   status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1752                 image->rows);
1753                   if (status == MagickFalse)
1754                     break;
1755                 }
1756             }
1757             Ascii85Flush(image);
1758             break;
1759           }
1760         }
1761       else
1762         {
1763           /*
1764             Dump number of colors and colormap.
1765           */
1766           switch (compression)
1767           {
1768             case RLECompression:
1769             default:
1770             {
1771               /*
1772                 Allocate pixel array.
1773               */
1774               length=(size_t) number_pixels;
1775               pixels=(unsigned char *) AcquireQuantumMemory(length,
1776                 sizeof(*pixels));
1777               if (pixels == (unsigned char *) NULL)
1778                 ThrowWriterException(ResourceLimitError,
1779                   "MemoryAllocationFailed");
1780               /*
1781                 Dump Runlength encoded pixels.
1782               */
1783               q=pixels;
1784               for (y=0; y < (ssize_t) image->rows; y++)
1785               {
1786                 p=GetVirtualPixels(image,0,y,image->columns,1,
1787                   &image->exception);
1788                 if (p == (const PixelPacket *) NULL)
1789                   break;
1790                 indexes=GetVirtualIndexQueue(image);
1791                 for (x=0; x < (ssize_t) image->columns; x++)
1792                   *q++=(unsigned char) indexes[x];
1793                 if (image->previous == (Image *) NULL)
1794                   {
1795                     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1796                 image->rows);
1797                     if (status == MagickFalse)
1798                       break;
1799                   }
1800               }
1801 #if defined(MAGICKCORE_ZLIB_DELEGATE)
1802               if (compression == ZipCompression)
1803                 status=ZLIBEncodeImage(image,length,pixels);
1804               else
1805 #endif
1806                 if (compression == LZWCompression)
1807                   status=LZWEncodeImage(image,length,pixels);
1808                 else
1809                   status=PackbitsEncodeImage(image,length,pixels);
1810               pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1811               if (status == MagickFalse)
1812                 {
1813                   (void) CloseBlob(image);
1814                   return(MagickFalse);
1815                 }
1816               break;
1817             }
1818             case NoCompression:
1819             {
1820               /*
1821                 Dump uncompressed PseudoColor packets.
1822               */
1823               Ascii85Initialize(image);
1824               for (y=0; y < (ssize_t) image->rows; y++)
1825               {
1826                 p=GetVirtualPixels(image,0,y,image->columns,1,
1827                   &image->exception);
1828                 if (p == (const PixelPacket *) NULL)
1829                   break;
1830                 indexes=GetVirtualIndexQueue(image);
1831                 for (x=0; x < (ssize_t) image->columns; x++)
1832                   Ascii85Encode(image,(unsigned char) indexes[x]);
1833                 if (image->previous == (Image *) NULL)
1834                   {
1835                     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1836                 image->rows);
1837                     if (status == MagickFalse)
1838                       break;
1839                   }
1840               }
1841               Ascii85Flush(image);
1842               break;
1843             }
1844           }
1845         }
1846     offset=TellBlob(image)-offset;
1847     (void) WriteBlobString(image,"\nendstream\n");
1848     (void) WriteBlobString(image,"endobj\n");
1849     /*
1850       Write Length object.
1851     */
1852     xref[object++]=TellBlob(image);
1853     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1854       object);
1855     (void) WriteBlobString(image,buffer);
1856     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",
1857       (double) offset);
1858     (void) WriteBlobString(image,buffer);
1859     (void) WriteBlobString(image,"endobj\n");
1860     /*
1861       Write Colorspace object.
1862     */
1863     xref[object++]=TellBlob(image);
1864     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1865       object);
1866     (void) WriteBlobString(image,buffer);
1867     if (image->colorspace == CMYKColorspace)
1868       (void) CopyMagickString(buffer,"/DeviceCMYK\n",MaxTextExtent);
1869     else
1870       if ((compression == FaxCompression) ||
1871           (compression == Group4Compression) ||
1872           ((image_info->type != TrueColorType) &&
1873            (IsGrayImage(image,&image->exception) != MagickFalse)))
1874           (void) CopyMagickString(buffer,"/DeviceGray\n",MaxTextExtent);
1875       else
1876         if ((image->storage_class == DirectClass) || (image->colors > 256) ||
1877             (compression == JPEGCompression) ||
1878             (compression == JPEG2000Compression))
1879           (void) CopyMagickString(buffer,"/DeviceRGB\n",MaxTextExtent);
1880         else
1881           (void) FormatMagickString(buffer,MaxTextExtent,
1882             "[ /Indexed /DeviceRGB %.20g %.20g 0 R ]\n",(double) image->colors-
1883             1,(double) object+3);
1884     (void) WriteBlobString(image,buffer);
1885     (void) WriteBlobString(image,"endobj\n");
1886     /*
1887       Write Thumb object.
1888     */
1889     SetGeometry(image,&geometry);
1890     (void) ParseMetaGeometry("106x106+0+0>",&geometry.x,&geometry.y,
1891       &geometry.width,&geometry.height);
1892     tile_image=ThumbnailImage(image,geometry.width,geometry.height,
1893       &image->exception);
1894     if (tile_image == (Image *) NULL)
1895       ThrowWriterException(ResourceLimitError,image->exception.reason);
1896     xref[object++]=TellBlob(image);
1897     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
1898       object);
1899     (void) WriteBlobString(image,buffer);
1900     (void) WriteBlobString(image,"<<\n");
1901     switch (compression)
1902     {
1903       case NoCompression:
1904       {
1905         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"ASCII85Decode");
1906         break;
1907       }
1908       case JPEGCompression:
1909       {
1910         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"DCTDecode");
1911         if (image->colorspace != CMYKColorspace)
1912           break;
1913         (void) WriteBlobString(image,buffer);
1914         (void) CopyMagickString(buffer,"/Decode [1 0 1 0 1 0 1 0]\n",
1915           MaxTextExtent);
1916         break;
1917       }
1918       case JPEG2000Compression:
1919       {
1920         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"JPXDecode");
1921         if (image->colorspace != CMYKColorspace)
1922           break;
1923         (void) WriteBlobString(image,buffer);
1924         (void) CopyMagickString(buffer,"/Decode [1 0 1 0 1 0 1 0]\n",
1925           MaxTextExtent);
1926         break;
1927       }
1928       case LZWCompression:
1929       {
1930         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"LZWDecode");
1931         break;
1932       }
1933       case ZipCompression:
1934       {
1935         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"FlateDecode");
1936         break;
1937       }
1938       case FaxCompression:
1939       case Group4Compression:
1940       {
1941         (void) CopyMagickString(buffer,"/Filter [ /CCITTFaxDecode ]\n",
1942           MaxTextExtent);
1943         (void) WriteBlobString(image,buffer);
1944         (void) FormatMagickString(buffer,MaxTextExtent,"/DecodeParms [ << "
1945           "/K %s /BlackIs1 false /Columns %.20g /Rows %.20g >> ]\n",CCITTParam,
1946           (double) tile_image->columns,(double) tile_image->rows);
1947         break;
1948       }
1949       default:
1950       {
1951         (void) FormatMagickString(buffer,MaxTextExtent,CFormat,
1952           "RunLengthDecode");
1953         break;
1954       }
1955     }
1956     (void) WriteBlobString(image,buffer);
1957     (void) FormatMagickString(buffer,MaxTextExtent,"/Width %.20g\n",(double)
1958       tile_image->columns);
1959     (void) WriteBlobString(image,buffer);
1960     (void) FormatMagickString(buffer,MaxTextExtent,"/Height %.20g\n",(double)
1961       tile_image->rows);
1962     (void) WriteBlobString(image,buffer);
1963     (void) FormatMagickString(buffer,MaxTextExtent,"/ColorSpace %.20g 0 R\n",
1964       (double) object-1);
1965     (void) WriteBlobString(image,buffer);
1966     (void) FormatMagickString(buffer,MaxTextExtent,"/BitsPerComponent %d\n",
1967       (compression == FaxCompression) || (compression == Group4Compression) ?
1968       1 : 8);
1969     (void) WriteBlobString(image,buffer);
1970     (void) FormatMagickString(buffer,MaxTextExtent,"/Length %.20g 0 R\n",
1971       (double) object+1);
1972     (void) WriteBlobString(image,buffer);
1973     (void) WriteBlobString(image,">>\n");
1974     (void) WriteBlobString(image,"stream\n");
1975     offset=TellBlob(image);
1976     number_pixels=(MagickSizeType) tile_image->columns*tile_image->rows;
1977     if ((compression == FaxCompression) ||
1978         (compression == Group4Compression) ||
1979         ((image_info->type != TrueColorType) &&
1980          (IsGrayImage(tile_image,&image->exception) != MagickFalse)))
1981       {
1982         switch (compression)
1983         {
1984           case FaxCompression:
1985           case Group4Compression:
1986           {
1987             if (LocaleCompare(CCITTParam,"0") == 0)
1988               {
1989                 (void) HuffmanEncodeImage(image_info,image,tile_image);
1990                 break;
1991               }
1992             (void) Huffman2DEncodeImage(image_info,image,tile_image);
1993             break;
1994           }
1995           case JPEGCompression:
1996           {
1997             status=InjectImageBlob(image_info,image,tile_image,"jpeg",
1998               &image->exception);
1999             if (status == MagickFalse)
2000               ThrowWriterException(CoderError,tile_image->exception.reason);
2001             break;
2002           }
2003           case JPEG2000Compression:
2004           {
2005             status=InjectImageBlob(image_info,image,tile_image,"jp2",
2006               &image->exception);
2007             if (status == MagickFalse)
2008               ThrowWriterException(CoderError,tile_image->exception.reason);
2009             break;
2010           }
2011           case RLECompression:
2012           default:
2013           {
2014             /*
2015               Allocate pixel array.
2016             */
2017             length=(size_t) number_pixels;
2018             pixels=(unsigned char *) AcquireQuantumMemory(length,
2019               sizeof(*pixels));
2020             if (pixels == (unsigned char *) NULL)
2021               {
2022                 tile_image=DestroyImage(tile_image);
2023                 ThrowWriterException(ResourceLimitError,
2024                   "MemoryAllocationFailed");
2025               }
2026             /*
2027               Dump Runlength encoded pixels.
2028             */
2029             q=pixels;
2030             for (y=0; y < (ssize_t) tile_image->rows; y++)
2031             {
2032               p=GetVirtualPixels(tile_image,0,y,tile_image->columns,1,
2033                 &tile_image->exception);
2034               if (p == (const PixelPacket *) NULL)
2035                 break;
2036               for (x=0; x < (ssize_t) tile_image->columns; x++)
2037               {
2038                 *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
2039                 p++;
2040               }
2041             }
2042 #if defined(MAGICKCORE_ZLIB_DELEGATE)
2043             if (compression == ZipCompression)
2044               status=ZLIBEncodeImage(image,length,pixels);
2045             else
2046 #endif
2047               if (compression == LZWCompression)
2048                 status=LZWEncodeImage(image,length,pixels);
2049               else
2050                 status=PackbitsEncodeImage(image,length,pixels);
2051             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2052             if (status == MagickFalse)
2053               {
2054                 (void) CloseBlob(image);
2055                 return(MagickFalse);
2056               }
2057             break;
2058           }
2059           case NoCompression:
2060           {
2061             /*
2062               Dump uncompressed PseudoColor packets.
2063             */
2064             Ascii85Initialize(image);
2065             for (y=0; y < (ssize_t) tile_image->rows; y++)
2066             {
2067               p=GetVirtualPixels(tile_image,0,y,tile_image->columns,1,
2068                 &tile_image->exception);
2069               if (p == (const PixelPacket *) NULL)
2070                 break;
2071               for (x=0; x < (ssize_t) tile_image->columns; x++)
2072               {
2073                 Ascii85Encode(image,
2074                   ScaleQuantumToChar(PixelIntensityToQuantum(p)));
2075                 p++;
2076               }
2077             }
2078             Ascii85Flush(image);
2079             break;
2080           }
2081         }
2082       }
2083     else
2084       if ((tile_image->storage_class == DirectClass) ||
2085           (tile_image->colors > 256) || (compression == JPEGCompression) ||
2086           (compression == JPEG2000Compression))
2087         switch (compression)
2088         {
2089           case JPEGCompression:
2090           {
2091             status=InjectImageBlob(image_info,image,tile_image,"jpeg",
2092               &image->exception);
2093             if (status == MagickFalse)
2094               ThrowWriterException(CoderError,tile_image->exception.reason);
2095             break;
2096           }
2097           case JPEG2000Compression:
2098           {
2099             status=InjectImageBlob(image_info,image,tile_image,"jp2",
2100               &image->exception);
2101             if (status == MagickFalse)
2102               ThrowWriterException(CoderError,tile_image->exception.reason);
2103             break;
2104           }
2105           case RLECompression:
2106           default:
2107           {
2108             /*
2109               Allocate pixel array.
2110             */
2111             length=(size_t) number_pixels;
2112             pixels=(unsigned char *) AcquireQuantumMemory(length,4*
2113               sizeof(*pixels));
2114             length*=tile_image->colorspace == CMYKColorspace ? 4UL : 3UL;
2115             if (pixels == (unsigned char *) NULL)
2116               {
2117                 tile_image=DestroyImage(tile_image);
2118                 ThrowWriterException(ResourceLimitError,
2119                   "MemoryAllocationFailed");
2120               }
2121             /*
2122               Dump runoffset encoded pixels.
2123             */
2124             q=pixels;
2125             for (y=0; y < (ssize_t) tile_image->rows; y++)
2126             {
2127               p=GetVirtualPixels(tile_image,0,y,tile_image->columns,1,
2128                 &tile_image->exception);
2129               if (p == (const PixelPacket *) NULL)
2130                 break;
2131               indexes=GetVirtualIndexQueue(tile_image);
2132               for (x=0; x < (ssize_t) tile_image->columns; x++)
2133               {
2134                 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
2135                 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
2136                 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
2137                 if (image->colorspace == CMYKColorspace)
2138                   *q++=ScaleQuantumToChar(indexes[x]);
2139                 p++;
2140               }
2141             }
2142 #if defined(MAGICKCORE_ZLIB_DELEGATE)
2143             if (compression == ZipCompression)
2144               status=ZLIBEncodeImage(image,length,pixels);
2145             else
2146 #endif
2147               if (compression == LZWCompression)
2148                 status=LZWEncodeImage(image,length,pixels);
2149               else
2150                 status=PackbitsEncodeImage(image,length,pixels);
2151             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2152             if (status == MagickFalse)
2153               {
2154                 (void) CloseBlob(image);
2155                 return(MagickFalse);
2156               }
2157             break;
2158           }
2159           case NoCompression:
2160           {
2161             /*
2162               Dump uncompressed DirectColor packets.
2163             */
2164             Ascii85Initialize(image);
2165             for (y=0; y < (ssize_t) tile_image->rows; y++)
2166             {
2167               p=GetVirtualPixels(tile_image,0,y,tile_image->columns,1,
2168                 &tile_image->exception);
2169               if (p == (const PixelPacket *) NULL)
2170                 break;
2171               indexes=GetVirtualIndexQueue(tile_image);
2172               for (x=0; x < (ssize_t) tile_image->columns; x++)
2173               {
2174                 Ascii85Encode(image,ScaleQuantumToChar(GetRedPixelComponent(p)));
2175                 Ascii85Encode(image,ScaleQuantumToChar(GetGreenPixelComponent(p)));
2176                 Ascii85Encode(image,ScaleQuantumToChar(GetBluePixelComponent(p)));
2177                 if (image->colorspace == CMYKColorspace)
2178                   Ascii85Encode(image,ScaleQuantumToChar(indexes[x]));
2179                 p++;
2180               }
2181             }
2182             Ascii85Flush(image);
2183             break;
2184           }
2185         }
2186       else
2187         {
2188           /*
2189             Dump number of colors and colormap.
2190           */
2191           switch (compression)
2192           {
2193             case RLECompression:
2194             default:
2195             {
2196               /*
2197                 Allocate pixel array.
2198               */
2199               length=(size_t) number_pixels;
2200               pixels=(unsigned char *) AcquireQuantumMemory(length,
2201                 sizeof(*pixels));
2202               if (pixels == (unsigned char *) NULL)
2203                 {
2204                   tile_image=DestroyImage(tile_image);
2205                   ThrowWriterException(ResourceLimitError,
2206                     "MemoryAllocationFailed");
2207                 }
2208               /*
2209                 Dump Runlength encoded pixels.
2210               */
2211               q=pixels;
2212               for (y=0; y < (ssize_t) tile_image->rows; y++)
2213               {
2214                 p=GetVirtualPixels(tile_image,0,y,tile_image->columns,1,
2215                   &tile_image->exception);
2216                 if (p == (const PixelPacket *) NULL)
2217                   break;
2218                 indexes=GetVirtualIndexQueue(tile_image);
2219                 for (x=0; x < (ssize_t) tile_image->columns; x++)
2220                   *q++=(unsigned char) indexes[x];
2221               }
2222 #if defined(MAGICKCORE_ZLIB_DELEGATE)
2223               if (compression == ZipCompression)
2224                 status=ZLIBEncodeImage(image,length,pixels);
2225               else
2226 #endif
2227                 if (compression == LZWCompression)
2228                   status=LZWEncodeImage(image,length,pixels);
2229                 else
2230                   status=PackbitsEncodeImage(image,length,pixels);
2231               pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2232               if (status == MagickFalse)
2233                 {
2234                   (void) CloseBlob(image);
2235                   return(MagickFalse);
2236                 }
2237               break;
2238             }
2239             case NoCompression:
2240             {
2241               /*
2242                 Dump uncompressed PseudoColor packets.
2243               */
2244               Ascii85Initialize(image);
2245               for (y=0; y < (ssize_t) tile_image->rows; y++)
2246               {
2247                 p=GetVirtualPixels(tile_image,0,y,tile_image->columns,1,
2248                   &tile_image->exception);
2249                 if (p == (const PixelPacket *) NULL)
2250                   break;
2251                 indexes=GetVirtualIndexQueue(tile_image);
2252                 for (x=0; x < (ssize_t) tile_image->columns; x++)
2253                   Ascii85Encode(image,(unsigned char) indexes[x]);
2254               }
2255               Ascii85Flush(image);
2256               break;
2257             }
2258           }
2259         }
2260     tile_image=DestroyImage(tile_image);
2261     offset=TellBlob(image)-offset;
2262     (void) WriteBlobString(image,"\nendstream\n");
2263     (void) WriteBlobString(image,"endobj\n");
2264     /*
2265       Write Length object.
2266     */
2267     xref[object++]=TellBlob(image);
2268     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
2269       object);
2270     (void) WriteBlobString(image,buffer);
2271     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",(double) offset);
2272     (void) WriteBlobString(image,buffer);
2273     (void) WriteBlobString(image,"endobj\n");
2274     xref[object++]=TellBlob(image);
2275     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
2276       object);
2277     (void) WriteBlobString(image,buffer);
2278     if ((image->storage_class != DirectClass) && (image->colors <= 256) &&
2279         (compression != FaxCompression) && (compression != Group4Compression))
2280       {
2281         /*
2282           Write Colormap object.
2283         */
2284         (void) WriteBlobString(image,"<<\n");
2285         if (compression == NoCompression)
2286           (void) WriteBlobString(image,"/Filter [ /ASCII85Decode ]\n");
2287         (void) FormatMagickString(buffer,MaxTextExtent,"/Length %.20g 0 R\n",
2288           (double) object+1);
2289         (void) WriteBlobString(image,buffer);
2290         (void) WriteBlobString(image,">>\n");
2291         (void) WriteBlobString(image,"stream\n");
2292         offset=TellBlob(image);
2293         if (compression == NoCompression)
2294           Ascii85Initialize(image);
2295         for (i=0; i < (ssize_t) image->colors; i++)
2296         {
2297           if (compression == NoCompression)
2298             {
2299               Ascii85Encode(image,ScaleQuantumToChar(image->colormap[i].red));
2300               Ascii85Encode(image,ScaleQuantumToChar(image->colormap[i].green));
2301               Ascii85Encode(image,ScaleQuantumToChar(image->colormap[i].blue));
2302               continue;
2303             }
2304           (void) WriteBlobByte(image,
2305             ScaleQuantumToChar(image->colormap[i].red));
2306           (void) WriteBlobByte(image,
2307             ScaleQuantumToChar(image->colormap[i].green));
2308           (void) WriteBlobByte(image,
2309             ScaleQuantumToChar(image->colormap[i].blue));
2310         }
2311         if (compression == NoCompression)
2312           Ascii85Flush(image);
2313        offset=TellBlob(image)-offset;
2314        (void) WriteBlobString(image,"\nendstream\n");
2315       }
2316     (void) WriteBlobString(image,"endobj\n");
2317     /*
2318       Write Length object.
2319     */
2320     xref[object++]=TellBlob(image);
2321     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
2322       object);
2323     (void) WriteBlobString(image,buffer);
2324     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",(double)
2325       offset);
2326     (void) WriteBlobString(image,buffer);
2327     (void) WriteBlobString(image,"endobj\n");
2328     /*
2329       Write softmask object.
2330     */
2331     xref[object++]=TellBlob(image);
2332     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
2333       object);
2334     (void) WriteBlobString(image,buffer);
2335     (void) WriteBlobString(image,"<<\n");
2336     if (image->matte == MagickFalse)
2337       (void) WriteBlobString(image,">>\n");
2338     else
2339       {
2340         (void) WriteBlobString(image,"/Type /XObject\n");
2341         (void) WriteBlobString(image,"/Subtype /Image\n");
2342         (void) FormatMagickString(buffer,MaxTextExtent,"/Name /Ma%.20g\n",
2343           (double) image->scene);
2344         (void) WriteBlobString(image,buffer);
2345         switch (compression)
2346         {
2347           case NoCompression:
2348           {
2349             (void) FormatMagickString(buffer,MaxTextExtent,CFormat,
2350               "ASCII85Decode");
2351             break;
2352           }
2353           case LZWCompression:
2354           {
2355             (void) FormatMagickString(buffer,MaxTextExtent,CFormat,"LZWDecode");
2356             break;
2357           }
2358           case ZipCompression:
2359           {
2360             (void) FormatMagickString(buffer,MaxTextExtent,CFormat,
2361               "FlateDecode");
2362             break;
2363           }
2364           default:
2365           {
2366             (void) FormatMagickString(buffer,MaxTextExtent,CFormat,
2367               "RunLengthDecode");
2368             break;
2369           }
2370         }
2371         (void) WriteBlobString(image,buffer);
2372         (void) FormatMagickString(buffer,MaxTextExtent,"/Width %.20g\n",
2373           (double) image->columns);
2374         (void) WriteBlobString(image,buffer);
2375         (void) FormatMagickString(buffer,MaxTextExtent,"/Height %.20g\n",
2376           (double) image->rows);
2377         (void) WriteBlobString(image,buffer);
2378         (void) WriteBlobString(image,"/ColorSpace /DeviceGray\n");
2379         (void) FormatMagickString(buffer,MaxTextExtent,"/BitsPerComponent %d\n",
2380           (compression == FaxCompression) || (compression == Group4Compression)
2381           ? 1 : 8);
2382         (void) WriteBlobString(image,buffer);
2383         (void) FormatMagickString(buffer,MaxTextExtent,"/Length %.20g 0 R\n",
2384           (double) object+1);
2385         (void) WriteBlobString(image,buffer);
2386         (void) WriteBlobString(image,">>\n");
2387         (void) WriteBlobString(image,"stream\n");
2388         offset=TellBlob(image);
2389         number_pixels=(MagickSizeType) image->columns*image->rows;
2390         switch (compression)
2391         {
2392           case RLECompression:
2393           default:
2394           {
2395             /*
2396               Allocate pixel array.
2397             */
2398             length=(size_t) number_pixels;
2399             pixels=(unsigned char *) AcquireQuantumMemory(length,
2400               sizeof(*pixels));
2401             if (pixels == (unsigned char *) NULL)
2402               {
2403                 image=DestroyImage(image);
2404                 ThrowWriterException(ResourceLimitError,
2405                   "MemoryAllocationFailed");
2406               }
2407             /*
2408               Dump Runlength encoded pixels.
2409             */
2410             q=pixels;
2411             for (y=0; y < (ssize_t) image->rows; y++)
2412             {
2413               p=GetVirtualPixels(image,0,y,image->columns,1,
2414                 &image->exception);
2415               if (p == (const PixelPacket *) NULL)
2416                 break;
2417               for (x=0; x < (ssize_t) image->columns; x++)
2418               {
2419                 *q++=ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p)));
2420                 p++;
2421               }
2422             }
2423 #if defined(MAGICKCORE_ZLIB_DELEGATE)
2424             if (compression == ZipCompression)
2425               status=ZLIBEncodeImage(image,length,pixels);
2426             else
2427 #endif
2428               if (compression == LZWCompression)
2429                 status=LZWEncodeImage(image,length,pixels);
2430               else
2431                 status=PackbitsEncodeImage(image,length,pixels);
2432             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
2433             if (status == MagickFalse)
2434               {
2435                 (void) CloseBlob(image);
2436                 return(MagickFalse);
2437               }
2438             break;
2439           }
2440           case NoCompression:
2441           {
2442             /*
2443               Dump uncompressed PseudoColor packets.
2444             */
2445             Ascii85Initialize(image);
2446             for (y=0; y < (ssize_t) image->rows; y++)
2447             {
2448               p=GetVirtualPixels(image,0,y,image->columns,1,
2449                 &image->exception);
2450               if (p == (const PixelPacket *) NULL)
2451                 break;
2452               for (x=0; x < (ssize_t) image->columns; x++)
2453               {
2454                 Ascii85Encode(image,ScaleQuantumToChar((Quantum) (QuantumRange-
2455                   GetOpacityPixelComponent(p))));
2456                 p++;
2457               }
2458             }
2459             Ascii85Flush(image);
2460             break;
2461           }
2462         }
2463         offset=TellBlob(image)-offset;
2464         (void) WriteBlobString(image,"\nendstream\n");
2465       }
2466     (void) WriteBlobString(image,"endobj\n");
2467     /*
2468       Write Length object.
2469     */
2470     xref[object++]=TellBlob(image);
2471     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
2472       object);
2473     (void) WriteBlobString(image,buffer);
2474     (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",(double) offset);
2475     (void) WriteBlobString(image,buffer);
2476     (void) WriteBlobString(image,"endobj\n");
2477     if (GetNextImageInList(image) == (Image *) NULL)
2478       break;
2479     image=SyncNextImageInList(image);
2480     status=SetImageProgress(image,SaveImagesTag,scene++,
2481       GetImageListLength(image));
2482     if (status == MagickFalse)
2483       break;
2484   } while (image_info->adjoin != MagickFalse);
2485   /*
2486     Write Metadata object.
2487   */
2488   xref[object++]=TellBlob(image);
2489   info_id=object;
2490   (void) FormatMagickString(buffer,MaxTextExtent,"%.20g 0 obj\n",(double)
2491     object);
2492   (void) WriteBlobString(image,buffer);
2493   (void) WriteBlobString(image,"<<\n");
2494   (void) FormatMagickString(buffer,MaxTextExtent,"/Title (%s)\n",
2495     EscapeParenthesis(image->filename));
2496   (void) WriteBlobString(image,buffer);
2497   seconds=time((time_t *) NULL);
2498 #if defined(MAGICKCORE_HAVE_LOCALTIME_R)
2499   (void) localtime_r(&seconds,&local_time);
2500 #else
2501   (void) memcpy(&local_time,localtime(&seconds),sizeof(local_time));
2502 #endif
2503   (void) FormatMagickString(date,MaxTextExtent,"D:%04d%02d%02d%02d%02d%02d",
2504     local_time.tm_year+1900,local_time.tm_mon+1,local_time.tm_mday,
2505     local_time.tm_hour,local_time.tm_min,local_time.tm_sec);
2506   (void) FormatMagickString(buffer,MaxTextExtent,"/CreationDate (%s)\n",date);
2507   (void) WriteBlobString(image,buffer);
2508   (void) FormatMagickString(buffer,MaxTextExtent,"/ModDate (%s)\n",date);
2509   (void) WriteBlobString(image,buffer);
2510   (void) FormatMagickString(buffer,MaxTextExtent,"/Producer (%s)\n",
2511     EscapeParenthesis(GetMagickVersion((size_t *) NULL)));
2512   (void) WriteBlobString(image,buffer);
2513   (void) WriteBlobString(image,">>\n");
2514   (void) WriteBlobString(image,"endobj\n");
2515   /*
2516     Write Xref object.
2517   */
2518   offset=TellBlob(image)-xref[0]+10;
2519   (void) WriteBlobString(image,"xref\n");
2520   (void) FormatMagickString(buffer,MaxTextExtent,"0 %.20g\n",(double)
2521     object+1);
2522   (void) WriteBlobString(image,buffer);
2523   (void) WriteBlobString(image,"0000000000 65535 f \n");
2524   for (i=0; i < (ssize_t) object; i++)
2525   {
2526     (void) FormatMagickString(buffer,MaxTextExtent,"%010lu 00000 n \n",
2527       (unsigned long) xref[i]);
2528     (void) WriteBlobString(image,buffer);
2529   }
2530   (void) WriteBlobString(image,"trailer\n");
2531   (void) WriteBlobString(image,"<<\n");
2532   (void) FormatMagickString(buffer,MaxTextExtent,"/Size %.20g\n",(double)
2533     object+1);
2534   (void) WriteBlobString(image,buffer);
2535   (void) FormatMagickString(buffer,MaxTextExtent,"/Info %.20g 0 R\n",(double)
2536     info_id);
2537   (void) WriteBlobString(image,buffer);
2538   (void) FormatMagickString(buffer,MaxTextExtent,"/Root %.20g 0 R\n",(double)
2539     root_id);
2540   (void) WriteBlobString(image,buffer);
2541   (void) WriteBlobString(image,">>\n");
2542   (void) WriteBlobString(image,"startxref\n");
2543   (void) FormatMagickString(buffer,MaxTextExtent,"%.20g\n",(double) offset);
2544   (void) WriteBlobString(image,buffer);
2545   (void) WriteBlobString(image,"%%EOF\n");
2546   xref=(MagickOffsetType *) RelinquishMagickMemory(xref);
2547   (void) CloseBlob(image);
2548   return(MagickTrue);
2549 }