xps.c revision 51816569c8796e1dd7ab1690bebd542faae51204
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-2015 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 "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/color.h"
47#include "MagickCore/color-private.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/constitute.h"
50#include "MagickCore/delegate.h"
51#include "MagickCore/draw.h"
52#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/geometry.h"
55#include "MagickCore/image.h"
56#include "MagickCore/image-private.h"
57#include "MagickCore/list.h"
58#include "MagickCore/magick.h"
59#include "MagickCore/memory_.h"
60#include "MagickCore/monitor.h"
61#include "MagickCore/monitor-private.h"
62#include "MagickCore/option.h"
63#include "MagickCore/profile.h"
64#include "MagickCore/property.h"
65#include "MagickCore/resource_.h"
66#include "MagickCore/quantum-private.h"
67#include "MagickCore/static.h"
68#include "MagickCore/string_.h"
69#include "MagickCore/module.h"
70#include "MagickCore/token.h"
71#include "MagickCore/transform.h"
72#include "MagickCore/utility.h"
73
74/*
75%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76%                                                                             %
77%                                                                             %
78%                                                                             %
79%   R e a d X P S I m a g e                                                   %
80%                                                                             %
81%                                                                             %
82%                                                                             %
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84%
85%  ReadXPSImage() reads a Printer Control Language image file and returns it.
86%  It allocates the memory necessary for the new Image structure and returns a
87%  pointer to the new image.
88%
89%  The format of the ReadXPSImage method is:
90%
91%      Image *ReadXPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
92%
93%  A description of each parameter follows:
94%
95%    o image_info: the image info.
96%
97%    o exception: return any errors or warnings in this structure.
98%
99*/
100static Image *ReadXPSImage(const ImageInfo *image_info,ExceptionInfo *exception)
101{
102#define CropBox  "CropBox"
103#define DeviceCMYK  "DeviceCMYK"
104#define MediaBox  "MediaBox"
105#define RenderXPSText  "  Rendering XPS...  "
106
107  char
108    command[MaxTextExtent],
109    *density,
110    filename[MaxTextExtent],
111    geometry[MaxTextExtent],
112    *options,
113    input_filename[MaxTextExtent];
114
115  const char
116    *option;
117
118  const DelegateInfo
119    *delegate_info;
120
121  Image
122    *image,
123    *next_image;
124
125  ImageInfo
126    *read_info;
127
128  MagickBooleanType
129    cmyk,
130    status;
131
132  PointInfo
133    delta;
134
135  RectangleInfo
136    bounding_box,
137    page;
138
139  register char
140    *p;
141
142  register ssize_t
143    c;
144
145  SegmentInfo
146    bounds;
147
148  size_t
149    height,
150    width;
151
152  ssize_t
153    count;
154
155  assert(image_info != (const ImageInfo *) NULL);
156  assert(image_info->signature == MagickSignature);
157  if (image_info->debug != MagickFalse)
158    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
159      image_info->filename);
160  assert(exception != (ExceptionInfo *) NULL);
161  assert(exception->signature == MagickSignature);
162  /*
163    Open image file.
164  */
165  image=AcquireImage(image_info,exception);
166  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
167  if (status == MagickFalse)
168    {
169      image=DestroyImageList(image);
170      return((Image *) NULL);
171    }
172  status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
173  if (status == MagickFalse)
174    {
175      ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
176        image_info->filename);
177      image=DestroyImageList(image);
178      return((Image *) NULL);
179    }
180  /*
181    Set the page density.
182  */
183  delta.x=DefaultResolution;
184  delta.y=DefaultResolution;
185  if ((image->resolution.x == 0.0) || (image->resolution.y == 0.0))
186    {
187      GeometryInfo
188        geometry_info;
189
190      MagickStatusType
191        flags;
192
193      flags=ParseGeometry(PSDensityGeometry,&geometry_info);
194      image->resolution.x=geometry_info.rho;
195      image->resolution.y=geometry_info.sigma;
196      if ((flags & SigmaValue) == 0)
197        image->resolution.y=image->resolution.x;
198    }
199  /*
200    Determine page geometry from the XPS media box.
201  */
202  cmyk=image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
203  count=0;
204  (void) ResetMagickMemory(&bounding_box,0,sizeof(bounding_box));
205  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
206  (void) ResetMagickMemory(&page,0,sizeof(page));
207  (void) ResetMagickMemory(command,0,sizeof(command));
208  p=command;
209  for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
210  {
211    if (image_info->page != (char *) NULL)
212      continue;
213    /*
214      Note XPS elements.
215    */
216    *p++=(char) c;
217    if ((c != (int) '/') && (c != '\n') &&
218        ((size_t) (p-command) < (MaxTextExtent-1)))
219      continue;
220    *p='\0';
221    p=command;
222    /*
223      Is this a CMYK document?
224    */
225    if (LocaleNCompare(DeviceCMYK,command,strlen(DeviceCMYK)) == 0)
226      cmyk=MagickTrue;
227    if (LocaleNCompare(CropBox,command,strlen(CropBox)) == 0)
228      {
229        /*
230          Note region defined by crop box.
231        */
232        count=(ssize_t) sscanf(command,"CropBox [%lf %lf %lf %lf",
233          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
234        if (count != 4)
235          count=(ssize_t) sscanf(command,"CropBox[%lf %lf %lf %lf",
236            &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
237      }
238    if (LocaleNCompare(MediaBox,command,strlen(MediaBox)) == 0)
239      {
240        /*
241          Note region defined by media box.
242        */
243        count=(ssize_t) sscanf(command,"MediaBox [%lf %lf %lf %lf",
244          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
245        if (count != 4)
246          count=(ssize_t) sscanf(command,"MediaBox[%lf %lf %lf %lf",
247            &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
248      }
249    if (count != 4)
250      continue;
251    /*
252      Set XPS render geometry.
253    */
254    width=(size_t) (floor(bounds.x2+0.5)-ceil(bounds.x1-0.5));
255    height=(size_t) (floor(bounds.y2+0.5)-ceil(bounds.y1-0.5));
256    if (width > page.width)
257      page.width=width;
258    if (height > page.height)
259      page.height=height;
260  }
261  (void) CloseBlob(image);
262  /*
263    Render XPS with the GhostXPS delegate.
264  */
265  if ((page.width == 0) || (page.height == 0))
266    (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
267  if (image_info->page != (char *) NULL)
268    (void) ParseAbsoluteGeometry(image_info->page,&page);
269  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g",(double)
270    page.width,(double) page.height);
271  if (image_info->monochrome != MagickFalse)
272    delegate_info=GetDelegateInfo("xps:mono",(char *) NULL,exception);
273  else
274     if (cmyk != MagickFalse)
275       delegate_info=GetDelegateInfo("xps:cmyk",(char *) NULL,exception);
276     else
277       delegate_info=GetDelegateInfo("xps:color",(char *) NULL,exception);
278  if (delegate_info == (const DelegateInfo *) NULL)
279    return((Image *) NULL);
280  density=AcquireString("");
281  options=AcquireString("");
282  (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",
283    image->resolution.x,image->resolution.y);
284  if ((page.width == 0) || (page.height == 0))
285    (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
286  if (image_info->page != (char *) NULL)
287    (void) ParseAbsoluteGeometry(image_info->page,&page);
288  page.width=(size_t) floor(page.width*image->resolution.y/delta.x+0.5);
289  page.height=(size_t) floor(page.height*image->resolution.y/delta.y+0.5);
290  (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
291    page.width,(double) page.height);
292  image=DestroyImage(image);
293  read_info=CloneImageInfo(image_info);
294  *read_info->magick='\0';
295  if (read_info->number_scenes != 0)
296    {
297      if (read_info->number_scenes != 1)
298        (void) FormatLocaleString(options,MaxTextExtent,"-dLastPage=%.20g",
299          (double) (read_info->scene+read_info->number_scenes));
300      else
301        (void) FormatLocaleString(options,MaxTextExtent,
302          "-dFirstPage=%.20g -dLastPage=%.20g",(double) read_info->scene+1,
303          (double) (read_info->scene+read_info->number_scenes));
304      read_info->number_scenes=0;
305      if (read_info->scenes != (char *) NULL)
306        *read_info->scenes='\0';
307    }
308  option=GetImageOption(image_info,"authenticate");
309  if (option != (const char *) NULL)
310    (void) FormatLocaleString(options+strlen(options),MaxTextExtent,
311      " -sPCLPassword=%s",option);
312  (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
313  (void) AcquireUniqueFilename(read_info->filename);
314  (void) FormatLocaleString(command,MaxTextExtent,
315    GetDelegateCommands(delegate_info),
316    read_info->antialias != MagickFalse ? 4 : 1,
317    read_info->antialias != MagickFalse ? 4 : 1,density,options,
318    read_info->filename,input_filename);
319  options=DestroyString(options);
320  density=DestroyString(density);
321  status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command,
322    (char *) NULL,exception) != 0 ? MagickTrue : MagickFalse;
323  image=ReadImage(read_info,exception);
324  (void) RelinquishUniqueFileResource(read_info->filename);
325  (void) RelinquishUniqueFileResource(input_filename);
326  read_info=DestroyImageInfo(read_info);
327  if (image == (Image *) NULL)
328    ThrowReaderException(DelegateError,"XPSDelegateFailed");
329  if (LocaleCompare(image->magick,"BMP") == 0)
330    {
331      Image
332        *cmyk_image;
333
334      cmyk_image=ConsolidateCMYKImages(image,exception);
335      if (cmyk_image != (Image *) NULL)
336        {
337          image=DestroyImageList(image);
338          image=cmyk_image;
339        }
340    }
341  do
342  {
343    (void) CopyMagickString(image->filename,filename,MaxTextExtent);
344    image->page=page;
345    next_image=SyncNextImageInList(image);
346    if (next_image != (Image *) NULL)
347      image=next_image;
348  } while (next_image != (Image *) NULL);
349  return(GetFirstImageInList(image));
350}
351
352/*
353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
354%                                                                             %
355%                                                                             %
356%                                                                             %
357%   R e g i s t e r X P S I m a g e                                           %
358%                                                                             %
359%                                                                             %
360%                                                                             %
361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362%
363%  RegisterXPSImage() adds attributes for the Microsoft XML Paper Specification
364%  format to the list of supported formats.  The attributes include the image
365%  format tag, a method to read and/or write the format, whether the format
366%  supports the saving of more than one frame to the same file or blob,
367%  whether the format supports native in-memory I/O, and a brief
368%  description of the format.
369%
370%  The format of the RegisterXPSImage method is:
371%
372%      size_t RegisterXPSImage(void)
373%
374*/
375ModuleExport size_t RegisterXPSImage(void)
376{
377  MagickInfo
378    *entry;
379
380  entry=AcquireMagickInfo("XPS","XPS","Microsoft XML Paper Specification");
381  entry->decoder=(DecodeImageHandler *) ReadXPSImage;
382  entry->flags^=CoderAdjoinFlag;
383  entry->flags^=CoderBlobSupportFlag;
384  entry->flags^=CoderDecoderThreadSupportFlag;
385  entry->flags|=CoderSeekableStreamFlag;
386  (void) RegisterMagickInfo(entry);
387  return(MagickImageCoderSignature);
388}
389
390/*
391%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
392%                                                                             %
393%                                                                             %
394%                                                                             %
395%   U n r e g i s t e r X P S I m a g e                                       %
396%                                                                             %
397%                                                                             %
398%                                                                             %
399%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400%
401%  UnregisterXPSImage() removes format registrations made by the XPS module
402%  from the list of supported formats.
403%
404%  The format of the UnregisterXPSImage method is:
405%
406%      UnregisterXPSImage(void)
407%
408*/
409ModuleExport void UnregisterXPSImage(void)
410{
411  (void) UnregisterMagickInfo("XPS");
412}
413