1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            X   X  PPPP   SSSSS                              %
7 %                             X X   P   P  SS                                 %
8 %                              X    PPPP    SSS                               %
9 %                             X X   P         SS                              %
10 %                            X   X  P      SSSSS                              %
11 %                                                                             %
12 %                                                                             %
13 %             Read/Write Microsoft XML Paper Specification Format             %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                January 2008                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 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 %    https://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 "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/blob-private.h"
47 #include "MagickCore/cache.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/color-private.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/constitute.h"
53 #include "MagickCore/delegate.h"
54 #include "MagickCore/delegate-private.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/exception.h"
57 #include "MagickCore/exception-private.h"
58 #include "MagickCore/geometry.h"
59 #include "MagickCore/image.h"
60 #include "MagickCore/image-private.h"
61 #include "MagickCore/list.h"
62 #include "MagickCore/magick.h"
63 #include "MagickCore/memory_.h"
64 #include "MagickCore/module.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/nt-base-private.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/profile.h"
70 #include "MagickCore/resource_.h"
71 #include "MagickCore/pixel-accessor.h"
72 #include "MagickCore/property.h"
73 #include "MagickCore/quantum-private.h"
74 #include "MagickCore/static.h"
75 #include "MagickCore/string_.h"
76 #include "MagickCore/string-private.h"
77 #include "MagickCore/timer-private.h"
78 #include "MagickCore/token.h"
79 #include "MagickCore/transform.h"
80 #include "MagickCore/utility.h"
81 #include "coders/bytebuffer-private.h"
82 #include "coders/ghostscript-private.h"
83 
84 /*
85   Typedef declaractions.
86 */
87 typedef struct _XPSInfo
88 {
89   MagickBooleanType
90     cmyk;
91 
92   SegmentInfo
93     bounds;
94 
95   unsigned long
96     columns,
97     rows;
98 
99   StringInfo
100     *icc_profile,
101     *photoshop_profile,
102     *xmp_profile;
103 } XPSInfo;
104 
105 /*
106 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107 %                                                                             %
108 %                                                                             %
109 %                                                                             %
110 %   R e a d X P S I m a g e                                                   %
111 %                                                                             %
112 %                                                                             %
113 %                                                                             %
114 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 %
116 %  ReadXPSImage() reads a Printer Control Language image file and returns it.
117 %  It allocates the memory necessary for the new Image structure and returns a
118 %  pointer to the new image.
119 %
120 %  The format of the ReadPSImage method is:
121 %
122 %      Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
123 %
124 %  A description of each parameter follows:
125 %
126 %    o image_info: the image info.
127 %
128 %    o exception: return any errors or warnings in this structure.
129 %
130 */
131 
ReadXPSImage(const ImageInfo * image_info,ExceptionInfo * exception)132 static Image *ReadXPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
133 {
134   char
135     command[MagickPathExtent],
136     *density,
137     filename[MagickPathExtent],
138     input_filename[MagickPathExtent],
139     message[MagickPathExtent],
140     *options;
141 
142   const char
143     *option;
144 
145   const DelegateInfo
146     *delegate_info;
147 
148   GeometryInfo
149     geometry_info;
150 
151   Image
152     *image,
153     *next,
154     *postscript_image;
155 
156   ImageInfo
157     *read_info;
158 
159   MagickBooleanType
160     fitPage,
161     status;
162 
163   MagickStatusType
164     flags;
165 
166   PointInfo
167     delta,
168     resolution;
169 
170   RectangleInfo
171     page;
172 
173   ssize_t
174     i;
175 
176   unsigned long
177     scene;
178 
179   /*
180     Open image file.
181   */
182   assert(image_info != (const ImageInfo *) NULL);
183   assert(image_info->signature == MagickCoreSignature);
184   if (image_info->debug != MagickFalse)
185     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
186       image_info->filename);
187   assert(exception != (ExceptionInfo *) NULL);
188   assert(exception->signature == MagickCoreSignature);
189   image=AcquireImage(image_info,exception);
190   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
191   if (status == MagickFalse)
192     {
193       image=DestroyImageList(image);
194       return((Image *) NULL);
195     }
196   status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
197   if (status == MagickFalse)
198     {
199       ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
200         image_info->filename);
201       image=DestroyImageList(image);
202       return((Image *) NULL);
203     }
204   /*
205     Set the page density.
206   */
207   delta.x=DefaultResolution;
208   delta.y=DefaultResolution;
209   if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
210     {
211       flags=ParseGeometry(PSDensityGeometry,&geometry_info);
212       image->resolution.x=geometry_info.rho;
213       image->resolution.y=geometry_info.sigma;
214       if ((flags & SigmaValue) == 0)
215         image->resolution.y=image->resolution.x;
216     }
217   if (image_info->density != (char *) NULL)
218     {
219       flags=ParseGeometry(image_info->density,&geometry_info);
220       image->resolution.x=geometry_info.rho;
221       image->resolution.y=geometry_info.sigma;
222       if ((flags & SigmaValue) == 0)
223         image->resolution.y=image->resolution.x;
224     }
225   (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
226   if (image_info->page != (char *) NULL)
227     (void) ParseAbsoluteGeometry(image_info->page,&page);
228   resolution=image->resolution;
229   page.width=(size_t) ((ssize_t) ceil((double) (page.width*resolution.x/
230     delta.x)-0.5));
231   page.height=(size_t) ((ssize_t) ceil((double) (page.height*resolution.y/
232     delta.y)-0.5));
233   fitPage=MagickFalse;
234   option=GetImageOption(image_info,"xps:fit-page");
235   if (option != (char *) NULL)
236     {
237       char
238         *page_geometry;
239 
240       page_geometry=GetPageGeometry(option);
241       flags=ParseMetaGeometry(page_geometry,&page.x,&page.y,&page.width,
242         &page.height);
243       if (flags == NoValue)
244         {
245           (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
246             "InvalidGeometry","`%s'",option);
247           page_geometry=DestroyString(page_geometry);
248           image=DestroyImage(image);
249           return((Image *) NULL);
250         }
251       page.width=(size_t) ((ssize_t) ceil((double) (page.width*
252         image->resolution.x/delta.x)-0.5));
253       page.height=(size_t) ((ssize_t) ceil((double) (page.height*
254         image->resolution.y/delta.y) -0.5));
255       page_geometry=DestroyString(page_geometry);
256       fitPage=MagickTrue;
257     }
258   /*
259     Render Postscript with the Ghostscript delegate.
260   */
261   delegate_info=GetDelegateInfo("xps:color",(char *) NULL,exception);
262   if (delegate_info == (const DelegateInfo *) NULL)
263     {
264       image=DestroyImageList(image);
265       return((Image *) NULL);
266     }
267   density=AcquireString("");
268   options=AcquireString("");
269   (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",resolution.x,
270     resolution.y);
271   if (image_info->ping != MagickFalse)
272     (void) FormatLocaleString(density,MagickPathExtent,"2.0x2.0");
273   (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ",(double)
274     page.width,(double) page.height);
275   read_info=CloneImageInfo(image_info);
276   *read_info->magick='\0';
277   if (read_info->number_scenes != 0)
278     {
279       char
280         pages[MagickPathExtent];
281 
282       (void) FormatLocaleString(pages,MagickPathExtent,"-dFirstPage=%.20g "
283         "-dLastPage=%.20g ",(double) read_info->scene+1,(double)
284         (read_info->scene+read_info->number_scenes));
285       (void) ConcatenateMagickString(options,pages,MagickPathExtent);
286       read_info->number_scenes=0;
287       if (read_info->scenes != (char *) NULL)
288         *read_info->scenes='\0';
289     }
290   if (*image_info->magick == 'E')
291     {
292       option=GetImageOption(image_info,"xps:use-cropbox");
293       if ((option == (const char *) NULL) ||
294           (IsStringTrue(option) != MagickFalse))
295         (void) ConcatenateMagickString(options,"-dEPSCrop ",MagickPathExtent);
296       if (fitPage != MagickFalse)
297         (void) ConcatenateMagickString(options,"-dEPSFitPage ",
298           MagickPathExtent);
299     }
300   (void) AcquireUniqueFilename(read_info->filename);
301   (void) RelinquishUniqueFileResource(read_info->filename);
302   (void) ConcatenateMagickString(read_info->filename,"%d",MagickPathExtent);
303   (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
304   (void) FormatLocaleString(command,MagickPathExtent,
305     GetDelegateCommands(delegate_info),
306     read_info->antialias != MagickFalse ? 4 : 1,
307     read_info->antialias != MagickFalse ? 4 : 1,density,options,
308     read_info->filename,input_filename);
309   options=DestroyString(options);
310   density=DestroyString(density);
311   *message='\0';
312   status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command,
313     (char *) NULL,exception) != 0 ? MagickTrue : MagickFalse;
314   (void) RelinquishUniqueFileResource(input_filename);
315   postscript_image=(Image *) NULL;
316   if (status == MagickFalse)
317     for (i=1; ; i++)
318     {
319       (void) InterpretImageFilename(image_info,image,filename,(int) i,
320         read_info->filename,exception);
321       if (IsGhostscriptRendered(read_info->filename) == MagickFalse)
322         break;
323       read_info->blob=NULL;
324       read_info->length=0;
325       next=ReadImage(read_info,exception);
326       (void) RelinquishUniqueFileResource(read_info->filename);
327       if (next == (Image *) NULL)
328         break;
329       AppendImageToList(&postscript_image,next);
330     }
331   else
332     for (i=1; ; i++)
333     {
334       (void) InterpretImageFilename(image_info,image,filename,(int) i,
335         read_info->filename,exception);
336       if (IsGhostscriptRendered(read_info->filename) == MagickFalse)
337         break;
338       read_info->blob=NULL;
339       read_info->length=0;
340       next=ReadImage(read_info,exception);
341       (void) RelinquishUniqueFileResource(read_info->filename);
342       if (next == (Image *) NULL)
343         break;
344       AppendImageToList(&postscript_image,next);
345     }
346   (void) RelinquishUniqueFileResource(filename);
347   read_info=DestroyImageInfo(read_info);
348   if (postscript_image == (Image *) NULL)
349     {
350       if (*message != '\0')
351         (void) ThrowMagickException(exception,GetMagickModule(),
352           DelegateError,"PostscriptDelegateFailed","`%s'",message);
353       image=DestroyImageList(image);
354       return((Image *) NULL);
355     }
356   if (LocaleCompare(postscript_image->magick,"BMP") == 0)
357     {
358       Image
359         *cmyk_image;
360 
361       cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
362       if (cmyk_image != (Image *) NULL)
363         {
364           postscript_image=DestroyImageList(postscript_image);
365           postscript_image=cmyk_image;
366         }
367     }
368   if (image_info->number_scenes != 0)
369     {
370       Image
371         *clone_image;
372 
373       /*
374         Add place holder images to meet the subimage specification requirement.
375       */
376       for (i=0; i < (ssize_t) image_info->scene; i++)
377       {
378         clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception);
379         if (clone_image != (Image *) NULL)
380           PrependImageToList(&postscript_image,clone_image);
381       }
382     }
383   do
384   {
385     (void) CopyMagickString(postscript_image->filename,filename,
386       MagickPathExtent);
387     (void) CopyMagickString(postscript_image->magick,image->magick,
388       MagickPathExtent);
389     postscript_image->page=page;
390     if (image_info->ping != MagickFalse)
391       {
392         postscript_image->magick_columns*=image->resolution.x/2.0;
393         postscript_image->magick_rows*=image->resolution.y/2.0;
394         postscript_image->columns*=image->resolution.x/2.0;
395         postscript_image->rows*=image->resolution.y/2.0;
396       }
397     (void) CloneImageProfiles(postscript_image,image);
398     (void) CloneImageProperties(postscript_image,image);
399     next=SyncNextImageInList(postscript_image);
400     if (next != (Image *) NULL)
401       postscript_image=next;
402   } while (next != (Image *) NULL);
403   image=DestroyImageList(image);
404   scene=0;
405   for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; )
406   {
407     next->scene=scene++;
408     next=GetNextImageInList(next);
409   }
410   return(GetFirstImageInList(postscript_image));
411 }
412 
413 /*
414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415 %                                                                             %
416 %                                                                             %
417 %                                                                             %
418 %   R e g i s t e r X P S I m a g e                                           %
419 %                                                                             %
420 %                                                                             %
421 %                                                                             %
422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423 %
424 %  RegisterXPSImage() adds properties for the PS image format to
425 %  the list of supported formats.  The properties include the image format
426 %  tag, a method to read and/or write the format, whether the format
427 %  supports the saving of more than one frame to the same file or blob,
428 %  whether the format supports native in-memory I/O, and a brief
429 %  description of the format.
430 %
431 %  The format of the RegisterXPSImage method is:
432 %
433 %      size_t RegisterXPSImage(void)
434 %
435 */
RegisterXPSImage(void)436 ModuleExport size_t RegisterXPSImage(void)
437 {
438   MagickInfo
439     *entry;
440 
441   entry=AcquireMagickInfo("XPS","XPS","Microsoft XML Paper Specification");
442   entry->decoder=(DecodeImageHandler *) ReadXPSImage;
443   entry->flags|=CoderDecoderSeekableStreamFlag;
444   entry->flags^=CoderAdjoinFlag;
445   entry->flags^=CoderBlobSupportFlag;
446   entry->mime_type=ConstantString("application/oxps");
447   (void) RegisterMagickInfo(entry);
448   return(MagickImageCoderSignature);
449 }
450 
451 /*
452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
453 %                                                                             %
454 %                                                                             %
455 %                                                                             %
456 %   U n r e g i s t e r X P S I m a g e                                       %
457 %                                                                             %
458 %                                                                             %
459 %                                                                             %
460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461 %
462 %  UnregisterXPSImage() removes format registrations made by the
463 %  XPS module from the list of supported formats.
464 %
465 %  The format of the UnregisterXPSImage method is:
466 %
467 %      UnregisterXPSImage(void)
468 %
469 */
UnregisterXPSImage(void)470 ModuleExport void UnregisterXPSImage(void)
471 {
472   (void) UnregisterMagickInfo("XPS");
473 }
474