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