xps.c revision dfc19b6079b83bb1129ec78b4426a120c0636e4d
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-2014 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[MaxTextExtent],
110    filename[MaxTextExtent],
111    geometry[MaxTextExtent],
112    options[MaxTextExtent],
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  (void) FormatLocaleString(density,MaxTextExtent,"%gx%g",
200    image->resolution.x,image->resolution.y);
201  /*
202    Determine page geometry from the XPS media box.
203  */
204  cmyk=image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse;
205  count=0;
206  (void) ResetMagickMemory(&bounding_box,0,sizeof(bounding_box));
207  (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
208  (void) ResetMagickMemory(&page,0,sizeof(page));
209  (void) ResetMagickMemory(command,0,sizeof(command));
210  p=command;
211  for (c=ReadBlobByte(image); c != EOF; c=ReadBlobByte(image))
212  {
213    if (image_info->page != (char *) NULL)
214      continue;
215    /*
216      Note XPS elements.
217    */
218    *p++=(char) c;
219    if ((c != (int) '/') && (c != '\n') &&
220        ((size_t) (p-command) < (MaxTextExtent-1)))
221      continue;
222    *p='\0';
223    p=command;
224    /*
225      Is this a CMYK document?
226    */
227    if (LocaleNCompare(DeviceCMYK,command,strlen(DeviceCMYK)) == 0)
228      cmyk=MagickTrue;
229    if (LocaleNCompare(CropBox,command,strlen(CropBox)) == 0)
230      {
231        /*
232          Note region defined by crop box.
233        */
234        count=(ssize_t) sscanf(command,"CropBox [%lf %lf %lf %lf",
235          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
236        if (count != 4)
237          count=(ssize_t) sscanf(command,"CropBox[%lf %lf %lf %lf",
238            &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
239      }
240    if (LocaleNCompare(MediaBox,command,strlen(MediaBox)) == 0)
241      {
242        /*
243          Note region defined by media box.
244        */
245        count=(ssize_t) sscanf(command,"MediaBox [%lf %lf %lf %lf",
246          &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
247        if (count != 4)
248          count=(ssize_t) sscanf(command,"MediaBox[%lf %lf %lf %lf",
249            &bounds.x1,&bounds.y1,&bounds.x2,&bounds.y2);
250      }
251    if (count != 4)
252      continue;
253    /*
254      Set XPS render geometry.
255    */
256    width=(size_t) (floor(bounds.x2+0.5)-ceil(bounds.x1-0.5));
257    height=(size_t) (floor(bounds.y2+0.5)-ceil(bounds.y1-0.5));
258    if (width > page.width)
259      page.width=width;
260    if (height > page.height)
261      page.height=height;
262  }
263  (void) CloseBlob(image);
264  /*
265    Render XPS with the GhostXPS delegate.
266  */
267  if ((page.width == 0) || (page.height == 0))
268    (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
269  if (image_info->page != (char *) NULL)
270    (void) ParseAbsoluteGeometry(image_info->page,&page);
271  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g",(double)
272    page.width,(double) page.height);
273  if (image_info->monochrome != MagickFalse)
274    delegate_info=GetDelegateInfo("xps:mono",(char *) NULL,exception);
275  else
276     if (cmyk != MagickFalse)
277       delegate_info=GetDelegateInfo("xps:cmyk",(char *) NULL,exception);
278     else
279       delegate_info=GetDelegateInfo("xps:color",(char *) NULL,exception);
280  if (delegate_info == (const DelegateInfo *) NULL)
281    return((Image *) NULL);
282  *options='\0';
283  if ((page.width == 0) || (page.height == 0))
284    (void) ParseAbsoluteGeometry(PSPageGeometry,&page);
285  if (image_info->page != (char *) NULL)
286    (void) ParseAbsoluteGeometry(image_info->page,&page);
287  page.width=(size_t) floor(page.width*image->resolution.y/delta.x+0.5);
288  page.height=(size_t) floor(page.height*image->resolution.y/delta.y+0.5);
289  (void) FormatLocaleString(options,MaxTextExtent,"-g%.20gx%.20g ",(double)
290    page.width,(double) page.height);
291  image=DestroyImage(image);
292  read_info=CloneImageInfo(image_info);
293  *read_info->magick='\0';
294  if (read_info->number_scenes != 0)
295    {
296      if (read_info->number_scenes != 1)
297        (void) FormatLocaleString(options,MaxTextExtent,"-dLastPage=%.20g",
298          (double) (read_info->scene+read_info->number_scenes));
299      else
300        (void) FormatLocaleString(options,MaxTextExtent,
301          "-dFirstPage=%.20g -dLastPage=%.20g",(double) read_info->scene+1,
302          (double) (read_info->scene+read_info->number_scenes));
303      read_info->number_scenes=0;
304      if (read_info->scenes != (char *) NULL)
305        *read_info->scenes='\0';
306    }
307  option=GetImageOption(image_info,"authenticate");
308  if (option != (const char *) NULL)
309    (void) FormatLocaleString(options+strlen(options),MaxTextExtent,
310      " -sPCLPassword=%s",option);
311  (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
312  (void) AcquireUniqueFilename(read_info->filename);
313  (void) FormatLocaleString(command,MaxTextExtent,
314    GetDelegateCommands(delegate_info),
315    read_info->antialias != MagickFalse ? 4 : 1,
316    read_info->antialias != MagickFalse ? 4 : 1,density,options,
317    read_info->filename,input_filename);
318  status=ExternalDelegateCommand(MagickFalse,read_info->verbose,command,
319    (char *) NULL,exception) != 0 ? MagickTrue : MagickFalse;
320  image=ReadImage(read_info,exception);
321  (void) RelinquishUniqueFileResource(read_info->filename);
322  (void) RelinquishUniqueFileResource(input_filename);
323  read_info=DestroyImageInfo(read_info);
324  if (image == (Image *) NULL)
325    ThrowReaderException(DelegateError,"XPSDelegateFailed");
326  if (LocaleCompare(image->magick,"BMP") == 0)
327    {
328      Image
329        *cmyk_image;
330
331      cmyk_image=ConsolidateCMYKImages(image,exception);
332      if (cmyk_image != (Image *) NULL)
333        {
334          image=DestroyImageList(image);
335          image=cmyk_image;
336        }
337    }
338  do
339  {
340    (void) CopyMagickString(image->filename,filename,MaxTextExtent);
341    image->page=page;
342    next_image=SyncNextImageInList(image);
343    if (next_image != (Image *) NULL)
344      image=next_image;
345  } while (next_image != (Image *) NULL);
346  return(GetFirstImageInList(image));
347}
348
349/*
350%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
351%                                                                             %
352%                                                                             %
353%                                                                             %
354%   R e g i s t e r X P S I m a g e                                           %
355%                                                                             %
356%                                                                             %
357%                                                                             %
358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
359%
360%  RegisterXPSImage() adds attributes for the Microsoft XML Paper Specification
361%  format to the list of supported formats.  The attributes include the image
362%  format tag, a method to read and/or write the format, whether the format
363%  supports the saving of more than one frame to the same file or blob,
364%  whether the format supports native in-memory I/O, and a brief
365%  description of the format.
366%
367%  The format of the RegisterXPSImage method is:
368%
369%      size_t RegisterXPSImage(void)
370%
371*/
372ModuleExport size_t RegisterXPSImage(void)
373{
374  MagickInfo
375    *entry;
376
377  entry=SetMagickInfo("XPS");
378  entry->decoder=(DecodeImageHandler *) ReadXPSImage;
379  entry->adjoin=MagickFalse;
380  entry->blob_support=MagickFalse;
381  entry->seekable_stream=MagickTrue;
382  entry->thread_support=EncoderThreadSupport;
383  entry->description=ConstantString("Microsoft XML Paper Specification");
384  entry->module=ConstantString("XPS");
385  (void) RegisterMagickInfo(entry);
386  return(MagickImageCoderSignature);
387}
388
389/*
390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391%                                                                             %
392%                                                                             %
393%                                                                             %
394%   U n r e g i s t e r X P S I m a g e                                       %
395%                                                                             %
396%                                                                             %
397%                                                                             %
398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399%
400%  UnregisterXPSImage() removes format registrations made by the XPS module
401%  from the list of supported formats.
402%
403%  The format of the UnregisterXPSImage method is:
404%
405%      UnregisterXPSImage(void)
406%
407*/
408ModuleExport void UnregisterXPSImage(void)
409{
410  (void) UnregisterMagickInfo("XPS");
411}
412