caption.c revision e67f43f8af5feb0f08d04b14dce7ccc162973715
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%               CCCC   AAA   PPPP   TTTTT  IIIII   OOO   N   N                %
7%              C      A   A  P   P    T      I    O   O  NN  N                %
8%              C      AAAAA  PPPP     T      I    O   O  N N N                %
9%              C      A   A  P        T      I    O   O  N  NN                %
10%               CCCC  A   A  P        T    IIIII   OOO   N   N                %
11%                                                                             %
12%                                                                             %
13%                             Read Text Caption.                              %
14%                                                                             %
15%                              Software Design                                %
16%                                John Cristy                                  %
17%                               February 2002                                 %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2013 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/annotate.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/blob-private.h"
47#include "MagickCore/composite-private.h"
48#include "MagickCore/draw.h"
49#include "MagickCore/draw-private.h"
50#include "MagickCore/exception.h"
51#include "MagickCore/exception-private.h"
52#include "MagickCore/image.h"
53#include "MagickCore/image-private.h"
54#include "MagickCore/list.h"
55#include "MagickCore/magick.h"
56#include "MagickCore/memory_.h"
57#include "MagickCore/module.h"
58#include "MagickCore/option.h"
59#include "MagickCore/property.h"
60#include "MagickCore/quantum-private.h"
61#include "MagickCore/static.h"
62#include "MagickCore/string_.h"
63#include "MagickCore/string-private.h"
64#include "MagickCore/utility.h"
65
66/*
67%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68%                                                                             %
69%                                                                             %
70%                                                                             %
71%   R e a d C A P T I O N I m a g e                                           %
72%                                                                             %
73%                                                                             %
74%                                                                             %
75%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76%
77%  ReadCAPTIONImage() reads a CAPTION image file and returns it.  It
78%  allocates the memory necessary for the new Image structure and returns a
79%  pointer to the new image.
80%
81%  The format of the ReadCAPTIONImage method is:
82%
83%      Image *ReadCAPTIONImage(const ImageInfo *image_info,
84%        ExceptionInfo *exception)
85%
86%  A description of each parameter follows:
87%
88%    o image_info: the image info.
89%
90%    o exception: return any errors or warnings in this structure.
91%
92*/
93static Image *ReadCAPTIONImage(const ImageInfo *image_info,
94  ExceptionInfo *exception)
95{
96  char
97    *caption,
98    geometry[MaxTextExtent],
99    *property,
100    *text;
101
102  const char
103    *gravity,
104    *option;
105
106  DrawInfo
107    *draw_info;
108
109  Image
110    *image;
111
112  MagickBooleanType
113    status;
114
115  register ssize_t
116    i;
117
118  size_t
119    height,
120    width;
121
122  TypeMetric
123    metrics;
124
125  /*
126    Initialize Image structure.
127  */
128  assert(image_info != (const ImageInfo *) NULL);
129  assert(image_info->signature == MagickSignature);
130  if (image_info->debug != MagickFalse)
131    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
132      image_info->filename);
133  assert(exception != (ExceptionInfo *) NULL);
134  assert(exception->signature == MagickSignature);
135  image=AcquireImage(image_info,exception);
136  (void) ResetImagePage(image,"0x0+0+0");
137  /*
138    Format caption.
139  */
140  option=GetImageOption(image_info,"filename");
141  if (option == (const char *) NULL)
142    property=InterpretImageProperties((ImageInfo *) image_info,image,
143      image_info->filename,exception);
144  else
145    if (LocaleNCompare(option,"caption:",8) == 0)
146      property=InterpretImageProperties((ImageInfo *) image_info,image,option+8,
147        exception);
148    else
149      property=InterpretImageProperties((ImageInfo *) image_info,image,option,
150        exception);
151  (void) SetImageProperty(image,"caption",property,exception);
152  property=DestroyString(property);
153  caption=ConstantString(GetImageProperty(image,"caption",exception));
154  draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
155  (void) CloneString(&draw_info->text,caption);
156  gravity=GetImageOption(image_info,"gravity");
157  if (gravity != (char *) NULL)
158    draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
159      MagickFalse,gravity);
160  if (image->columns == 0)
161    {
162      text=AcquireString(caption);
163      i=FormatMagickCaption(image,draw_info,MagickFalse,&metrics,&text,
164        exception);
165      (void) CloneString(&draw_info->text,text);
166      text=DestroyString(text);
167      (void) FormatLocaleString(geometry,MaxTextExtent,"%+g%+g",
168        -metrics.bounds.x1,metrics.ascent);
169      if (draw_info->gravity == UndefinedGravity)
170        (void) CloneString(&draw_info->geometry,geometry);
171      status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
172      width=(size_t) floor(metrics.width+draw_info->stroke_width+0.5);
173      image->columns=width;
174    }
175  if (image->rows == 0)
176    {
177      text=AcquireString(caption);
178      i=FormatMagickCaption(image,draw_info,MagickFalse,&metrics,&text,
179        exception);
180      (void) CloneString(&draw_info->text,text);
181      text=DestroyString(text);
182      (void) FormatLocaleString(geometry,MaxTextExtent,"%+g%+g",
183        -metrics.bounds.x1,metrics.ascent);
184      if (draw_info->gravity == UndefinedGravity)
185        (void) CloneString(&draw_info->geometry,geometry);
186      status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
187      image->rows=(size_t) ((i+1)*(metrics.ascent-metrics.descent+
188        draw_info->interline_spacing+draw_info->stroke_width)+0.5);
189    }
190  if (fabs(image_info->pointsize) < MagickEpsilon)
191    {
192      double
193        high,
194        low;
195
196      /*
197        Auto fit text into bounding box.
198      */
199      for ( ; ; draw_info->pointsize*=2.0)
200      {
201        text=AcquireString(caption);
202        i=FormatMagickCaption(image,draw_info,MagickFalse,&metrics,&text,
203          exception);
204        (void) CloneString(&draw_info->text,text);
205        text=DestroyString(text);
206        (void) FormatLocaleString(geometry,MaxTextExtent,"%+g%+g",
207          -metrics.bounds.x1,metrics.ascent);
208        if (draw_info->gravity == UndefinedGravity)
209          (void) CloneString(&draw_info->geometry,geometry);
210        status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
211        width=(size_t) floor(metrics.width+draw_info->stroke_width+0.5);
212        height=(size_t) floor(metrics.height+draw_info->stroke_width+0.5);
213        if ((width >= image->columns) && (height >= image->rows))
214          break;
215        if ((width >= (image->columns << 1)) || (height >= (image->rows << 1)))
216          break;
217      }
218      high=draw_info->pointsize;
219      for (low=1.0; (high-low) > 1.0; )
220      {
221        draw_info->pointsize=(low+high)/2.0;
222        text=AcquireString(caption);
223        i=FormatMagickCaption(image,draw_info,MagickFalse,&metrics,&text,
224          exception);
225        (void) CloneString(&draw_info->text,text);
226        text=DestroyString(text);
227        (void) FormatLocaleString(geometry,MaxTextExtent,"%+g%+g",
228          -metrics.bounds.x1,metrics.ascent);
229        if (draw_info->gravity == UndefinedGravity)
230          (void) CloneString(&draw_info->geometry,geometry);
231        status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
232        width=(size_t) floor(metrics.width+draw_info->stroke_width+0.5);
233        height=(size_t) floor(metrics.height+draw_info->stroke_width+0.5);
234        if ((width <= image->columns) && (height <= image->rows))
235          low=draw_info->pointsize+1.0;
236        else
237          high=draw_info->pointsize-1.0;
238      }
239      for (draw_info->pointsize=(low+high)/2.0; (high-low) > 1.0; )
240      {
241        text=AcquireString(caption);
242        i=FormatMagickCaption(image,draw_info,MagickFalse,&metrics,&text,
243          exception);
244        (void) CloneString(&draw_info->text,text);
245        text=DestroyString(text);
246        (void) FormatLocaleString(geometry,MaxTextExtent,"%+g%+g",
247          -metrics.bounds.x1,metrics.ascent);
248        if (draw_info->gravity == UndefinedGravity)
249          (void) CloneString(&draw_info->geometry,geometry);
250        status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
251        width=(size_t) floor(metrics.width+draw_info->stroke_width+0.5);
252        height=(size_t) floor(metrics.height+draw_info->stroke_width+0.5);
253        if ((width <= image->columns) && (height <= image->rows))
254          break;
255        draw_info->pointsize--;
256      }
257      draw_info->pointsize--;
258    }
259  (void) CloneString(&draw_info->text,caption);
260  i=FormatMagickCaption(image,draw_info,MagickFalse,&metrics,&caption,
261    exception);
262  if (SetImageBackgroundColor(image,exception) == MagickFalse)
263    {
264      image=DestroyImageList(image);
265      return((Image *) NULL);
266    }
267  /*
268    Draw caption.
269  */
270  (void) CloneString(&draw_info->text,caption);
271  status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
272  if ((draw_info->gravity != UndefinedGravity) &&
273      (draw_info->direction != RightToLeftDirection))
274    image->page.x=(ssize_t) (metrics.bounds.x1-draw_info->stroke_width/2.0);
275  else
276    {
277      (void) FormatLocaleString(geometry,MaxTextExtent,"%+g%+g",
278        -metrics.bounds.x1+draw_info->stroke_width/2.0,metrics.ascent+
279        draw_info->stroke_width/2.0);
280      if (draw_info->direction == RightToLeftDirection)
281        (void) FormatLocaleString(geometry,MaxTextExtent,"%+g%+g",
282          image->columns-(metrics.bounds.x2+draw_info->stroke_width/2.0),
283          metrics.ascent+draw_info->stroke_width/2.0);
284      draw_info->geometry=AcquireString(geometry);
285    }
286  status=AnnotateImage(image,draw_info,exception);
287  draw_info=DestroyDrawInfo(draw_info);
288  caption=DestroyString(caption);
289  if (status == MagickFalse)
290    {
291      image=DestroyImageList(image);
292      return((Image *) NULL);
293    }
294  return(GetFirstImageInList(image));
295}
296
297/*
298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
299%                                                                             %
300%                                                                             %
301%                                                                             %
302%   R e g i s t e r C A P T I O N I m a g e                                   %
303%                                                                             %
304%                                                                             %
305%                                                                             %
306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307%
308%  RegisterCAPTIONImage() adds attributes for the CAPTION image format to
309%  the list of supported formats.  The attributes include the image format
310%  tag, a method to read and/or write the format, whether the format
311%  supports the saving of more than one frame to the same file or blob,
312%  whether the format supports native in-memory I/O, and a brief
313%  description of the format.
314%
315%  The format of the RegisterCAPTIONImage method is:
316%
317%      size_t RegisterCAPTIONImage(void)
318%
319*/
320ModuleExport size_t RegisterCAPTIONImage(void)
321{
322  MagickInfo
323    *entry;
324
325  entry=SetMagickInfo("CAPTION");
326  entry->decoder=(DecodeImageHandler *) ReadCAPTIONImage;
327  entry->description=ConstantString("Caption");
328  entry->adjoin=MagickFalse;
329  entry->module=ConstantString("CAPTION");
330  (void) RegisterMagickInfo(entry);
331  return(MagickImageCoderSignature);
332}
333
334/*
335%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
336%                                                                             %
337%                                                                             %
338%                                                                             %
339%   U n r e g i s t e r C A P T I O N I m a g e                               %
340%                                                                             %
341%                                                                             %
342%                                                                             %
343%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344%
345%  UnregisterCAPTIONImage() removes format registrations made by the
346%  CAPTION module from the list of supported formats.
347%
348%  The format of the UnregisterCAPTIONImage method is:
349%
350%      UnregisterCAPTIONImage(void)
351%
352*/
353ModuleExport void UnregisterCAPTIONImage(void)
354{
355  (void) UnregisterMagickInfo("CAPTION");
356}
357