root / ImageMagick / trunk / coders / pdf.c

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