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