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