/src/imagemagick/coders/xps.c
Line | Count | Source |
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 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/license/ % |
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 declarations. |
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 | | |
132 | | static Image *ReadXPSImage(const ImageInfo *image_info,ExceptionInfo *exception) |
133 | 257 | { |
134 | 257 | char |
135 | 257 | command[MagickPathExtent], |
136 | 257 | *density, |
137 | 257 | filename[MagickPathExtent], |
138 | 257 | input_filename[MagickPathExtent], |
139 | 257 | message[MagickPathExtent], |
140 | 257 | *options; |
141 | | |
142 | 257 | const char |
143 | 257 | *option; |
144 | | |
145 | 257 | const DelegateInfo |
146 | 257 | *delegate_info; |
147 | | |
148 | 257 | GeometryInfo |
149 | 257 | geometry_info; |
150 | | |
151 | 257 | Image |
152 | 257 | *image, |
153 | 257 | *next, |
154 | 257 | *postscript_image; |
155 | | |
156 | 257 | ImageInfo |
157 | 257 | *read_info; |
158 | | |
159 | 257 | MagickBooleanType |
160 | 257 | fitPage, |
161 | 257 | status; |
162 | | |
163 | 257 | MagickStatusType |
164 | 257 | flags; |
165 | | |
166 | 257 | PointInfo |
167 | 257 | delta, |
168 | 257 | resolution; |
169 | | |
170 | 257 | RectangleInfo |
171 | 257 | page; |
172 | | |
173 | 257 | ssize_t |
174 | 257 | i; |
175 | | |
176 | 257 | unsigned long |
177 | 257 | scene; |
178 | | |
179 | | /* |
180 | | Open image file. |
181 | | */ |
182 | 257 | assert(image_info != (const ImageInfo *) NULL); |
183 | 257 | assert(image_info->signature == MagickCoreSignature); |
184 | 257 | assert(exception != (ExceptionInfo *) NULL); |
185 | 257 | assert(exception->signature == MagickCoreSignature); |
186 | 257 | if (IsEventLogging() != MagickFalse) |
187 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
188 | 0 | image_info->filename); |
189 | 257 | image=AcquireImage(image_info,exception); |
190 | 257 | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
191 | 257 | if (status == MagickFalse) |
192 | 0 | { |
193 | 0 | image=DestroyImageList(image); |
194 | 0 | return((Image *) NULL); |
195 | 0 | } |
196 | 257 | status=AcquireUniqueSymbolicLink(image_info->filename,input_filename); |
197 | 257 | if (status == MagickFalse) |
198 | 0 | { |
199 | 0 | ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile", |
200 | 0 | image_info->filename); |
201 | 0 | image=DestroyImageList(image); |
202 | 0 | return((Image *) NULL); |
203 | 0 | } |
204 | | /* |
205 | | Set the page density. |
206 | | */ |
207 | 257 | delta.x=DefaultResolution; |
208 | 257 | delta.y=DefaultResolution; |
209 | 257 | if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0)) |
210 | 257 | { |
211 | 257 | flags=ParseGeometry(PSDensityGeometry,&geometry_info); |
212 | 257 | if ((flags & RhoValue) != 0) |
213 | 257 | image->resolution.x=geometry_info.rho; |
214 | 257 | image->resolution.y=image->resolution.x; |
215 | 257 | if ((flags & SigmaValue) != 0) |
216 | 257 | image->resolution.y=geometry_info.sigma; |
217 | 257 | } |
218 | 257 | if (image_info->density != (char *) NULL) |
219 | 0 | { |
220 | 0 | flags=ParseGeometry(image_info->density,&geometry_info); |
221 | 0 | if ((flags & RhoValue) != 0) |
222 | 0 | image->resolution.x=geometry_info.rho; |
223 | 0 | image->resolution.y=image->resolution.x; |
224 | 0 | if ((flags & SigmaValue) != 0) |
225 | 0 | image->resolution.y=geometry_info.sigma; |
226 | 0 | } |
227 | 257 | (void) ParseAbsoluteGeometry(PSPageGeometry,&page); |
228 | 257 | if (image_info->page != (char *) NULL) |
229 | 0 | (void) ParseAbsoluteGeometry(image_info->page,&page); |
230 | 257 | resolution=image->resolution; |
231 | 257 | page.width=(size_t) ((ssize_t) ceil((double) (page.width*resolution.x/ |
232 | 257 | delta.x)-0.5)); |
233 | 257 | page.height=(size_t) ((ssize_t) ceil((double) (page.height*resolution.y/ |
234 | 257 | delta.y)-0.5)); |
235 | 257 | fitPage=MagickFalse; |
236 | 257 | option=GetImageOption(image_info,"xps:fit-page"); |
237 | 257 | if (option != (char *) NULL) |
238 | 0 | { |
239 | 0 | char |
240 | 0 | *page_geometry; |
241 | |
|
242 | 0 | page_geometry=GetPageGeometry(option); |
243 | 0 | flags=ParseMetaGeometry(page_geometry,&page.x,&page.y,&page.width, |
244 | 0 | &page.height); |
245 | 0 | if (flags == NoValue) |
246 | 0 | { |
247 | 0 | (void) ThrowMagickException(exception,GetMagickModule(),OptionError, |
248 | 0 | "InvalidGeometry","`%s'",option); |
249 | 0 | page_geometry=DestroyString(page_geometry); |
250 | 0 | image=DestroyImage(image); |
251 | 0 | return((Image *) NULL); |
252 | 0 | } |
253 | 0 | page.width=(size_t) ((ssize_t) ceil((double) (page.width* |
254 | 0 | image->resolution.x/delta.x)-0.5)); |
255 | 0 | page.height=(size_t) ((ssize_t) ceil((double) (page.height* |
256 | 0 | image->resolution.y/delta.y) -0.5)); |
257 | 0 | page_geometry=DestroyString(page_geometry); |
258 | 0 | fitPage=MagickTrue; |
259 | 0 | } |
260 | | /* |
261 | | Render Postscript with the Ghostscript delegate. |
262 | | */ |
263 | 257 | delegate_info=GetDelegateInfo("xps:color",(char *) NULL,exception); |
264 | 257 | if (delegate_info == (const DelegateInfo *) NULL) |
265 | 0 | { |
266 | 0 | image=DestroyImageList(image); |
267 | 0 | return((Image *) NULL); |
268 | 0 | } |
269 | 257 | density=AcquireString(""); |
270 | 257 | options=AcquireString(""); |
271 | 257 | (void) FormatLocaleString(density,MagickPathExtent,"%gx%g",resolution.x, |
272 | 257 | resolution.y); |
273 | 257 | if (image_info->ping != MagickFalse) |
274 | 0 | (void) FormatLocaleString(density,MagickPathExtent,"2.0x2.0"); |
275 | 257 | else |
276 | 257 | (void) FormatLocaleString(options,MagickPathExtent,"-g%.20gx%.20g ", |
277 | 257 | (double) page.width,(double) page.height); |
278 | 257 | read_info=CloneImageInfo(image_info); |
279 | 257 | *read_info->magick='\0'; |
280 | 257 | if (read_info->number_scenes != 0) |
281 | 0 | { |
282 | 0 | char |
283 | 0 | pages[MagickPathExtent]; |
284 | |
|
285 | 0 | (void) FormatLocaleString(pages,MagickPathExtent,"-dFirstPage=%.20g " |
286 | 0 | "-dLastPage=%.20g ",(double) read_info->scene+1,(double) |
287 | 0 | (read_info->scene+read_info->number_scenes)); |
288 | 0 | (void) ConcatenateMagickString(options,pages,MagickPathExtent); |
289 | 0 | read_info->number_scenes=0; |
290 | 0 | if (read_info->scenes != (char *) NULL) |
291 | 0 | *read_info->scenes='\0'; |
292 | 0 | } |
293 | 257 | if (*image_info->magick == 'E') |
294 | 0 | { |
295 | 0 | option=GetImageOption(image_info,"xps:use-cropbox"); |
296 | 0 | if ((option == (const char *) NULL) || |
297 | 0 | (IsStringTrue(option) != MagickFalse)) |
298 | 0 | (void) ConcatenateMagickString(options,"-dEPSCrop ",MagickPathExtent); |
299 | 0 | if (fitPage != MagickFalse) |
300 | 0 | (void) ConcatenateMagickString(options,"-dEPSFitPage ", |
301 | 0 | MagickPathExtent); |
302 | 0 | } |
303 | 257 | (void) AcquireUniqueFilename(read_info->filename); |
304 | 257 | (void) RelinquishUniqueFileResource(read_info->filename); |
305 | 257 | (void) ConcatenateMagickString(read_info->filename,"%d",MagickPathExtent); |
306 | 257 | (void) CopyMagickString(filename,read_info->filename,MagickPathExtent); |
307 | 257 | (void) FormatLocaleString(command,MagickPathExtent, |
308 | 257 | GetDelegateCommands(delegate_info), |
309 | 257 | read_info->antialias != MagickFalse ? 4 : 1, |
310 | 257 | read_info->antialias != MagickFalse ? 4 : 1,density,options, |
311 | 257 | read_info->filename,input_filename); |
312 | 257 | options=DestroyString(options); |
313 | 257 | density=DestroyString(density); |
314 | 257 | *message='\0'; |
315 | 257 | status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command, |
316 | 257 | (char *) NULL,exception) != 0 ? MagickTrue : MagickFalse; |
317 | 257 | (void) RelinquishUniqueFileResource(input_filename); |
318 | 257 | postscript_image=(Image *) NULL; |
319 | 257 | if (status == MagickFalse) |
320 | 0 | for (i=1; ; i++) |
321 | 0 | { |
322 | 0 | (void) InterpretImageFilename(image_info,image,filename,(int) i, |
323 | 0 | read_info->filename,exception); |
324 | 0 | if (IsGhostscriptRendered(read_info->filename) == MagickFalse) |
325 | 0 | break; |
326 | 0 | read_info->blob=NULL; |
327 | 0 | read_info->length=0; |
328 | 0 | next=ReadImage(read_info,exception); |
329 | 0 | (void) RelinquishUniqueFileResource(read_info->filename); |
330 | 0 | if (next == (Image *) NULL) |
331 | 0 | break; |
332 | 0 | AppendImageToList(&postscript_image,next); |
333 | 0 | } |
334 | 257 | (void) RelinquishUniqueFileResource(filename); |
335 | 257 | read_info=DestroyImageInfo(read_info); |
336 | 257 | if (postscript_image == (Image *) NULL) |
337 | 257 | { |
338 | 257 | if (*message != '\0') |
339 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
340 | 0 | DelegateError,"PostscriptDelegateFailed","`%s'",message); |
341 | 257 | image=DestroyImageList(image); |
342 | 257 | return((Image *) NULL); |
343 | 257 | } |
344 | 0 | if (LocaleCompare(postscript_image->magick,"BMP") == 0) |
345 | 0 | { |
346 | 0 | Image |
347 | 0 | *cmyk_image; |
348 | |
|
349 | 0 | cmyk_image=ConsolidateCMYKImages(postscript_image,exception); |
350 | 0 | if (cmyk_image != (Image *) NULL) |
351 | 0 | { |
352 | 0 | postscript_image=DestroyImageList(postscript_image); |
353 | 0 | postscript_image=cmyk_image; |
354 | 0 | } |
355 | 0 | } |
356 | 0 | if (image_info->number_scenes != 0) |
357 | 0 | { |
358 | 0 | Image |
359 | 0 | *clone_image; |
360 | | |
361 | | /* |
362 | | Add place holder images to meet the subimage specification requirement. |
363 | | */ |
364 | 0 | for (i=0; i < (ssize_t) image_info->scene; i++) |
365 | 0 | { |
366 | 0 | clone_image=CloneImage(postscript_image,1,1,MagickTrue,exception); |
367 | 0 | if (clone_image != (Image *) NULL) |
368 | 0 | PrependImageToList(&postscript_image,clone_image); |
369 | 0 | } |
370 | 0 | } |
371 | 0 | do |
372 | 0 | { |
373 | 0 | (void) CopyMagickString(postscript_image->filename,filename, |
374 | 0 | MagickPathExtent); |
375 | 0 | (void) CopyMagickString(postscript_image->magick,image->magick, |
376 | 0 | MagickPathExtent); |
377 | 0 | postscript_image->page=page; |
378 | 0 | if (image_info->ping != MagickFalse) |
379 | 0 | { |
380 | 0 | postscript_image->magick_columns=page.width; |
381 | 0 | postscript_image->magick_rows=page.height; |
382 | 0 | postscript_image->columns=page.width; |
383 | 0 | postscript_image->rows=page.height; |
384 | 0 | } |
385 | 0 | (void) CloneImageProfiles(postscript_image,image); |
386 | 0 | (void) CloneImageProperties(postscript_image,image); |
387 | 0 | next=SyncNextImageInList(postscript_image); |
388 | 0 | if (next != (Image *) NULL) |
389 | 0 | postscript_image=next; |
390 | 0 | } while (next != (Image *) NULL); |
391 | 0 | image=DestroyImageList(image); |
392 | 0 | scene=0; |
393 | 0 | for (next=GetFirstImageInList(postscript_image); next != (Image *) NULL; ) |
394 | 0 | { |
395 | 0 | next->scene=scene++; |
396 | 0 | next=GetNextImageInList(next); |
397 | 0 | } |
398 | 0 | return(GetFirstImageInList(postscript_image)); |
399 | 257 | } |
400 | | |
401 | | /* |
402 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
403 | | % % |
404 | | % % |
405 | | % % |
406 | | % R e g i s t e r X P S I m a g e % |
407 | | % % |
408 | | % % |
409 | | % % |
410 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
411 | | % |
412 | | % RegisterXPSImage() adds properties for the PS image format to |
413 | | % the list of supported formats. The properties include the image format |
414 | | % tag, a method to read and/or write the format, whether the format |
415 | | % supports the saving of more than one frame to the same file or blob, |
416 | | % whether the format supports native in-memory I/O, and a brief |
417 | | % description of the format. |
418 | | % |
419 | | % The format of the RegisterXPSImage method is: |
420 | | % |
421 | | % size_t RegisterXPSImage(void) |
422 | | % |
423 | | */ |
424 | | ModuleExport size_t RegisterXPSImage(void) |
425 | 8 | { |
426 | 8 | MagickInfo |
427 | 8 | *entry; |
428 | | |
429 | 8 | entry=AcquireMagickInfo("XPS","XPS","Microsoft XML Paper Specification"); |
430 | 8 | entry->decoder=(DecodeImageHandler *) ReadXPSImage; |
431 | 8 | entry->flags|=CoderDecoderSeekableStreamFlag; |
432 | 8 | entry->flags^=CoderAdjoinFlag; |
433 | 8 | entry->flags^=CoderBlobSupportFlag; |
434 | 8 | entry->mime_type=ConstantString("application/oxps"); |
435 | 8 | (void) RegisterMagickInfo(entry); |
436 | 8 | return(MagickImageCoderSignature); |
437 | 8 | } |
438 | | |
439 | | /* |
440 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
441 | | % % |
442 | | % % |
443 | | % % |
444 | | % U n r e g i s t e r X P S I m a g e % |
445 | | % % |
446 | | % % |
447 | | % % |
448 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
449 | | % |
450 | | % UnregisterXPSImage() removes format registrations made by the |
451 | | % XPS module from the list of supported formats. |
452 | | % |
453 | | % The format of the UnregisterXPSImage method is: |
454 | | % |
455 | | % UnregisterXPSImage(void) |
456 | | % |
457 | | */ |
458 | | ModuleExport void UnregisterXPSImage(void) |
459 | 0 | { |
460 | 0 | (void) UnregisterMagickInfo("XPS"); |
461 | 0 | } |