blob: 9471cd4fd56fd4d702b2fb07c4cb4278438c9845 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
Cristyc60d3282020-07-26 09:55:35 -040017% January 2008 %
cristy3ed852e2009-09-05 21:47:34 +000018% %
19% %
Cristyd8420112021-01-01 14:52:00 -050020% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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% %
Cristy83d74de2018-10-13 10:17:25 -040026% https://imagemagick.org/script/license.php %
cristy3ed852e2009-09-05 21:47:34 +000027% %
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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
cristyc6348612013-04-05 14:24:24 +000043#include "MagickCore/artifact.h"
Cristyc60d3282020-07-26 09:55:35 -040044#include "MagickCore/attribute.h"
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/blob.h"
46#include "MagickCore/blob-private.h"
Cristyc60d3282020-07-26 09:55:35 -040047#include "MagickCore/cache.h"
cristy4c08aed2011-07-01 19:47:50 +000048#include "MagickCore/color.h"
49#include "MagickCore/color-private.h"
50#include "MagickCore/colorspace.h"
Cristyc60d3282020-07-26 09:55:35 -040051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/constitute.h"
53#include "MagickCore/delegate.h"
Cristyc60d3282020-07-26 09:55:35 -040054#include "MagickCore/delegate-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#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"
Cristyc60d3282020-07-26 09:55:35 -040064#include "MagickCore/module.h"
cristy4c08aed2011-07-01 19:47:50 +000065#include "MagickCore/monitor.h"
66#include "MagickCore/monitor-private.h"
Cristyc60d3282020-07-26 09:55:35 -040067#include "MagickCore/nt-base-private.h"
cristyc689f1f2011-10-05 21:13:24 +000068#include "MagickCore/option.h"
cristy4c08aed2011-07-01 19:47:50 +000069#include "MagickCore/profile.h"
70#include "MagickCore/resource_.h"
Cristyc60d3282020-07-26 09:55:35 -040071#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/property.h"
cristy4c08aed2011-07-01 19:47:50 +000073#include "MagickCore/quantum-private.h"
74#include "MagickCore/static.h"
75#include "MagickCore/string_.h"
Cristyc60d3282020-07-26 09:55:35 -040076#include "MagickCore/string-private.h"
77#include "MagickCore/timer-private.h"
cristy4c08aed2011-07-01 19:47:50 +000078#include "MagickCore/token.h"
79#include "MagickCore/transform.h"
80#include "MagickCore/utility.h"
Cristyc60d3282020-07-26 09:55:35 -040081#include "coders/bytebuffer-private.h"
82#include "coders/ghostscript-private.h"
83
84/*
85 Typedef declaractions.
86*/
87typedef 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;
cristy3ed852e2009-09-05 21:47:34 +0000104
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%
Cristyc60d3282020-07-26 09:55:35 -0400120% The format of the ReadPSImage method is:
cristy3ed852e2009-09-05 21:47:34 +0000121%
Cristyc60d3282020-07-26 09:55:35 -0400122% Image *ReadPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000123%
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*/
Cristyc60d3282020-07-26 09:55:35 -0400131
cristy3ed852e2009-09-05 21:47:34 +0000132static Image *ReadXPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
133{
cristy3ed852e2009-09-05 21:47:34 +0000134 char
cristy151b66d2015-04-15 10:50:31 +0000135 command[MagickPathExtent],
cristy51816562015-04-12 13:18:38 +0000136 *density,
cristy151b66d2015-04-15 10:50:31 +0000137 filename[MagickPathExtent],
Cristyc60d3282020-07-26 09:55:35 -0400138 input_filename[MagickPathExtent],
139 message[MagickPathExtent],
140 *options;
141
142 const char
143 *option;
cristy3ed852e2009-09-05 21:47:34 +0000144
145 const DelegateInfo
146 *delegate_info;
147
Cristyc60d3282020-07-26 09:55:35 -0400148 GeometryInfo
149 geometry_info;
150
cristy3ed852e2009-09-05 21:47:34 +0000151 Image
152 *image,
Cristyc60d3282020-07-26 09:55:35 -0400153 *next,
154 *postscript_image;
cristy3ed852e2009-09-05 21:47:34 +0000155
156 ImageInfo
157 *read_info;
158
159 MagickBooleanType
Cristyc60d3282020-07-26 09:55:35 -0400160 fitPage,
cristy3ed852e2009-09-05 21:47:34 +0000161 status;
162
Cristyc60d3282020-07-26 09:55:35 -0400163 MagickStatusType
164 flags;
165
cristy3ed852e2009-09-05 21:47:34 +0000166 PointInfo
Cristyc60d3282020-07-26 09:55:35 -0400167 delta,
168 resolution;
cristy3ed852e2009-09-05 21:47:34 +0000169
170 RectangleInfo
cristy3ed852e2009-09-05 21:47:34 +0000171 page;
172
Cristyf2dc1dd2020-12-28 13:59:26 -0500173 ssize_t
Cristyc60d3282020-07-26 09:55:35 -0400174 i;
cristy3ed852e2009-09-05 21:47:34 +0000175
Cristyc60d3282020-07-26 09:55:35 -0400176 unsigned long
177 scene;
cristy3ed852e2009-09-05 21:47:34 +0000178
Cristyc60d3282020-07-26 09:55:35 -0400179 /*
180 Open image file.
181 */
cristy3ed852e2009-09-05 21:47:34 +0000182 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000183 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000184 if (image_info->debug != MagickFalse)
185 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
186 image_info->filename);
187 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000188 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000189 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000190 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;
cristy2a11bef2011-10-28 18:33:11 +0000209 if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
cristy3ed852e2009-09-05 21:47:34 +0000210 {
cristy3ed852e2009-09-05 21:47:34 +0000211 flags=ParseGeometry(PSDensityGeometry,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +0000212 image->resolution.x=geometry_info.rho;
213 image->resolution.y=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000214 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +0000215 image->resolution.y=image->resolution.x;
cristy3ed852e2009-09-05 21:47:34 +0000216 }
Cristyc60d3282020-07-26 09:55:35 -0400217 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);
cristy3ed852e2009-09-05 21:47:34 +0000226 if (image_info->page != (char *) NULL)
227 (void) ParseAbsoluteGeometry(image_info->page,&page);
Cristyc60d3282020-07-26 09:55:35 -0400228 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);
cristy3ed852e2009-09-05 21:47:34 +0000262 if (delegate_info == (const DelegateInfo *) NULL)
Cristy041b4c52018-02-04 15:11:57 -0500263 {
Cristyc60d3282020-07-26 09:55:35 -0400264 image=DestroyImageList(image);
Cristy041b4c52018-02-04 15:11:57 -0500265 return((Image *) NULL);
266 }
cristy51816562015-04-12 13:18:38 +0000267 density=AcquireString("");
268 options=AcquireString("");
Cristyc60d3282020-07-26 09:55:35 -0400269 (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",resolution.x,
270 resolution.y);
Cristy89c5c752020-12-18 13:04:39 +0000271 if (image_info->ping != MagickFalse)
272 (void) FormatLocaleString(density,MagickPathExtent,"2.0x2.0");
cristy151b66d2015-04-15 10:50:31 +0000273 (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ",(double)
cristye8c25f92010-06-03 00:53:06 +0000274 page.width,(double) page.height);
cristy3ed852e2009-09-05 21:47:34 +0000275 read_info=CloneImageInfo(image_info);
276 *read_info->magick='\0';
277 if (read_info->number_scenes != 0)
278 {
Cristyc60d3282020-07-26 09:55:35 -0400279 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);
cristy3ed852e2009-09-05 21:47:34 +0000286 read_info->number_scenes=0;
287 if (read_info->scenes != (char *) NULL)
288 *read_info->scenes='\0';
289 }
Cristyc60d3282020-07-26 09:55:35 -0400290 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 }
cristy3ed852e2009-09-05 21:47:34 +0000300 (void) AcquireUniqueFilename(read_info->filename);
Cristyc60d3282020-07-26 09:55:35 -0400301 (void) RelinquishUniqueFileResource(read_info->filename);
302 (void) ConcatenateMagickString(read_info->filename,"%d",MagickPathExtent);
303 (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
cristy151b66d2015-04-15 10:50:31 +0000304 (void) FormatLocaleString(command,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000305 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);
cristy51816562015-04-12 13:18:38 +0000309 options=DestroyString(options);
310 density=DestroyString(density);
Cristyc60d3282020-07-26 09:55:35 -0400311 *message='\0';
cristydfc19b62014-10-17 22:52:24 +0000312 status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command,
313 (char *) NULL,exception) != 0 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000314 (void) RelinquishUniqueFileResource(input_filename);
Cristyc60d3282020-07-26 09:55:35 -0400315 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);
cristy3ed852e2009-09-05 21:47:34 +0000347 read_info=DestroyImageInfo(read_info);
Cristyc60d3282020-07-26 09:55:35 -0400348 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)
cristy3ed852e2009-09-05 21:47:34 +0000357 {
358 Image
359 *cmyk_image;
360
Cristyc60d3282020-07-26 09:55:35 -0400361 cmyk_image=ConsolidateCMYKImages(postscript_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000362 if (cmyk_image != (Image *) NULL)
363 {
Cristyc60d3282020-07-26 09:55:35 -0400364 postscript_image=DestroyImageList(postscript_image);
365 postscript_image=cmyk_image;
cristy3ed852e2009-09-05 21:47:34 +0000366 }
367 }
Cristyc60d3282020-07-26 09:55:35 -0400368 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 }
cristy3ed852e2009-09-05 21:47:34 +0000383 do
384 {
Cristyc60d3282020-07-26 09:55:35 -0400385 (void) CopyMagickString(postscript_image->filename,filename,
386 MagickPathExtent);
387 (void) CopyMagickString(postscript_image->magick,image->magick,
388 MagickPathExtent);
389 postscript_image->page=page;
Cristy49d662c2020-12-17 15:48:09 +0000390 if (image_info->ping != MagickFalse)
391 {
Cristy89c5c752020-12-18 13:04:39 +0000392 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;
Cristy49d662c2020-12-17 15:48:09 +0000396 }
Cristyc60d3282020-07-26 09:55:35 -0400397 (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));
cristy3ed852e2009-09-05 21:47:34 +0000411}
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%
Cristyc60d3282020-07-26 09:55:35 -0400424% 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
cristy3ed852e2009-09-05 21:47:34 +0000427% 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%
cristybb503372010-05-27 20:51:26 +0000433% size_t RegisterXPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000434%
435*/
cristybb503372010-05-27 20:51:26 +0000436ModuleExport size_t RegisterXPSImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000437{
438 MagickInfo
439 *entry;
440
dirk06b627a2015-04-06 18:59:17 +0000441 entry=AcquireMagickInfo("XPS","XPS","Microsoft XML Paper Specification");
cristy3ed852e2009-09-05 21:47:34 +0000442 entry->decoder=(DecodeImageHandler *) ReadXPSImage;
Cristyc60d3282020-07-26 09:55:35 -0400443 entry->flags|=CoderDecoderSeekableStreamFlag;
dirk08e9a112015-02-22 01:51:41 +0000444 entry->flags^=CoderAdjoinFlag;
445 entry->flags^=CoderBlobSupportFlag;
Cristyc60d3282020-07-26 09:55:35 -0400446 entry->mime_type=ConstantString("application/oxps");
cristy3ed852e2009-09-05 21:47:34 +0000447 (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%
Cristyc60d3282020-07-26 09:55:35 -0400462% UnregisterXPSImage() removes format registrations made by the
463% XPS module from the list of supported formats.
cristy3ed852e2009-09-05 21:47:34 +0000464%
465% The format of the UnregisterXPSImage method is:
466%
467% UnregisterXPSImage(void)
468%
469*/
470ModuleExport void UnregisterXPSImage(void)
471{
472 (void) UnregisterMagickInfo("XPS");
473}