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 % Cristy %
17 % February 2002 %
18 % %
19 % %
20 % Copyright 1999-2021 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/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/resource_.h"
62 #include "MagickCore/static.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/string-private.h"
65 #include "MagickCore/utility.h"
66
67 /*
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69 % %
70 % %
71 % %
72 % R e a d C A P T I O N I m a g e %
73 % %
74 % %
75 % %
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 %
78 % ReadCAPTIONImage() reads a CAPTION image file and returns it. It
79 % allocates the memory necessary for the new Image structure and returns a
80 % pointer to the new image.
81 %
82 % The format of the ReadCAPTIONImage method is:
83 %
84 % Image *ReadCAPTIONImage(const ImageInfo *image_info,
85 % ExceptionInfo *exception)
86 %
87 % A description of each parameter follows:
88 %
89 % o image_info: the image info.
90 %
91 % o exception: return any errors or warnings in this structure.
92 %
93 */
ReadCAPTIONImage(const ImageInfo * image_info,ExceptionInfo * exception)94 static Image *ReadCAPTIONImage(const ImageInfo *image_info,
95 ExceptionInfo *exception)
96 {
97 char
98 *caption,
99 geometry[MagickPathExtent],
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 split,
114 status;
115
116 ssize_t
117 i;
118
119 size_t
120 height,
121 width;
122
123 TypeMetric
124 metrics;
125
126 /*
127 Initialize Image structure.
128 */
129 assert(image_info != (const ImageInfo *) NULL);
130 assert(image_info->signature == MagickCoreSignature);
131 if (image_info->debug != MagickFalse)
132 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
133 image_info->filename);
134 assert(exception != (ExceptionInfo *) NULL);
135 assert(exception->signature == MagickCoreSignature);
136 image=AcquireImage(image_info,exception);
137 (void) ResetImagePage(image,"0x0+0+0");
138 if ((image->columns != 0) && (image->rows != 0))
139 (void) SetImageBackgroundColor(image,exception);
140 /*
141 Format caption.
142 */
143 option=GetImageOption(image_info,"filename");
144 if (option == (const char *) NULL)
145 caption=InterpretImageProperties((ImageInfo *) image_info,image,
146 image_info->filename,exception);
147 else
148 if (LocaleNCompare(option,"caption:",8) == 0)
149 caption=InterpretImageProperties((ImageInfo *) image_info,image,option+8,
150 exception);
151 else
152 caption=InterpretImageProperties((ImageInfo *) image_info,image,option,
153 exception);
154 if (caption == (char *) NULL)
155 return(DestroyImageList(image));
156 (void) SetImageProperty(image,"caption",caption,exception);
157 draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
158 width=(size_t) floor(draw_info->pointsize*strlen(caption)+0.5);
159 if (AcquireMagickResource(WidthResource,width) == MagickFalse)
160 {
161 caption=DestroyString(caption);
162 draw_info=DestroyDrawInfo(draw_info);
163 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
164 }
165 (void) CloneString(&draw_info->text,caption);
166 gravity=GetImageOption(image_info,"gravity");
167 if (gravity != (char *) NULL)
168 draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions,
169 MagickFalse,gravity);
170 split=IsStringTrue(GetImageOption(image_info,"caption:split"));
171 status=MagickTrue;
172 (void) memset(&metrics,0,sizeof(metrics));
173 if (image->columns == 0)
174 {
175 text=AcquireString(caption);
176 i=FormatMagickCaption(image,draw_info,split,&metrics,&text,exception);
177 (void) CloneString(&draw_info->text,text);
178 text=DestroyString(text);
179 (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g",
180 -metrics.bounds.x1,metrics.ascent);
181 if (draw_info->gravity == UndefinedGravity)
182 (void) CloneString(&draw_info->geometry,geometry);
183 status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
184 image->columns=(size_t) floor(metrics.width+draw_info->stroke_width+0.5);
185 }
186 if (image->rows == 0)
187 {
188 split=MagickTrue;
189 text=AcquireString(caption);
190 i=FormatMagickCaption(image,draw_info,split,&metrics,&text,exception);
191 (void) CloneString(&draw_info->text,text);
192 text=DestroyString(text);
193 (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g",
194 -metrics.bounds.x1,metrics.ascent);
195 if (draw_info->gravity == UndefinedGravity)
196 (void) CloneString(&draw_info->geometry,geometry);
197 status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
198 image->rows=(size_t) ((i+1)*(metrics.ascent-metrics.descent+
199 draw_info->interline_spacing+draw_info->stroke_width)+0.5);
200 }
201 if (status != MagickFalse)
202 status=SetImageExtent(image,image->columns,image->rows,exception);
203 if (status == MagickFalse)
204 {
205 caption=DestroyString(caption);
206 draw_info=DestroyDrawInfo(draw_info);
207 return(DestroyImageList(image));
208 }
209 if (SetImageBackgroundColor(image,exception) == MagickFalse)
210 {
211 caption=DestroyString(caption);
212 draw_info=DestroyDrawInfo(draw_info);
213 image=DestroyImageList(image);
214 return((Image *) NULL);
215 }
216 if ((fabs(image_info->pointsize) < MagickEpsilon) && (strlen(caption) > 0))
217 {
218 double
219 high,
220 low;
221
222 ssize_t
223 n;
224
225 /*
226 Auto fit text into bounding box.
227 */
228 option=GetImageOption(image_info,"caption:max-pointsize");
229 if (option != (const char*) NULL)
230 {
231 high=StringToDouble(option,(char**) NULL);
232 if (high < 1.0)
233 high=1.0;
234 high+=1.0;
235 }
236 else
237 {
238 for (n=0; n < 32; n++, draw_info->pointsize*=2.0)
239 {
240 text=AcquireString(caption);
241 i=FormatMagickCaption(image,draw_info,split,&metrics,&text,
242 exception);
243 (void) CloneString(&draw_info->text,text);
244 text=DestroyString(text);
245 (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g",
246 -metrics.bounds.x1,metrics.ascent);
247 if (draw_info->gravity == UndefinedGravity)
248 (void) CloneString(&draw_info->geometry,geometry);
249 status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
250 if (status == MagickFalse)
251 break;
252 width=(size_t) floor(metrics.width+draw_info->stroke_width+0.5);
253 height=(size_t) floor(metrics.height+draw_info->interline_spacing+
254 draw_info->stroke_width+0.5);
255 if ((image->columns != 0) && (image->rows != 0))
256 {
257 if ((width >= image->columns) && (height >= image->rows))
258 break;
259 }
260 else
261 if (((image->columns != 0) && (width >= image->columns)) ||
262 ((image->rows != 0) && (height >= image->rows)))
263 break;
264 }
265 high=draw_info->pointsize;
266 }
267 for (low=1.0; (high-low) > 0.5; )
268 {
269 draw_info->pointsize=(low+high)/2.0;
270 text=AcquireString(caption);
271 i=FormatMagickCaption(image,draw_info,split,&metrics,&text,
272 exception);
273 (void) CloneString(&draw_info->text,text);
274 text=DestroyString(text);
275 (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g",
276 -metrics.bounds.x1,metrics.ascent);
277 if (draw_info->gravity == UndefinedGravity)
278 (void) CloneString(&draw_info->geometry,geometry);
279 status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception);
280 if (status == MagickFalse)
281 break;
282 width=(size_t) floor(metrics.width+draw_info->stroke_width+0.5);
283 height=(size_t) floor(metrics.height+draw_info->interline_spacing+
284 draw_info->stroke_width+0.5);
285 if ((image->columns != 0) && (image->rows != 0))
286 {
287 if ((width < image->columns) && (height < image->rows))
288 low=draw_info->pointsize+0.5;
289 else
290 high=draw_info->pointsize-0.5;
291 }
292 else
293 if (((image->columns != 0) && (width < image->columns)) ||
294 ((image->rows != 0) && (height < image->rows)))
295 low=draw_info->pointsize+0.5;
296 else
297 high=draw_info->pointsize-0.5;
298 }
299 draw_info->pointsize=floor((low+high)/2.0-0.5);
300 }
301 /*
302 Draw caption.
303 */
304 i=FormatMagickCaption(image,draw_info,split,&metrics,&caption,exception);
305 (void) CloneString(&draw_info->text,caption);
306 caption=DestroyString(caption);
307 (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g",MagickMax(
308 draw_info->direction == RightToLeftDirection ? (double) image->columns-
309 metrics.bounds.x2 : -metrics.bounds.x1,0.0),(draw_info->gravity ==
310 UndefinedGravity ? MagickMax(metrics.ascent,metrics.bounds.y2) : 0.0));
311 (void) CloneString(&draw_info->geometry,geometry);
312 status=AnnotateImage(image,draw_info,exception);
313 if (image_info->pointsize == 0.0)
314 (void) FormatImageProperty(image,"caption:pointsize","%.*g",
315 GetMagickPrecision(),draw_info->pointsize);
316 (void) FormatImageProperty(image,"caption:lines","%.*g",GetMagickPrecision(),
317 (double) (i+1));
318 draw_info=DestroyDrawInfo(draw_info);
319 if (status == MagickFalse)
320 {
321 image=DestroyImageList(image);
322 return((Image *) NULL);
323 }
324 return(GetFirstImageInList(image));
325 }
326
327 /*
328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329 % %
330 % %
331 % %
332 % R e g i s t e r C A P T I O N I m a g e %
333 % %
334 % %
335 % %
336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337 %
338 % RegisterCAPTIONImage() adds attributes for the CAPTION image format to
339 % the list of supported formats. The attributes include the image format
340 % tag, a method to read and/or write the format, whether the format
341 % supports the saving of more than one frame to the same file or blob,
342 % whether the format supports native in-memory I/O, and a brief
343 % description of the format.
344 %
345 % The format of the RegisterCAPTIONImage method is:
346 %
347 % size_t RegisterCAPTIONImage(void)
348 %
349 */
RegisterCAPTIONImage(void)350 ModuleExport size_t RegisterCAPTIONImage(void)
351 {
352 MagickInfo
353 *entry;
354
355 entry=AcquireMagickInfo("CAPTION","CAPTION","Caption");
356 entry->decoder=(DecodeImageHandler *) ReadCAPTIONImage;
357 entry->flags^=CoderAdjoinFlag;
358 (void) RegisterMagickInfo(entry);
359 return(MagickImageCoderSignature);
360 }
361
362 /*
363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
364 % %
365 % %
366 % %
367 % U n r e g i s t e r C A P T I O N I m a g e %
368 % %
369 % %
370 % %
371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372 %
373 % UnregisterCAPTIONImage() removes format registrations made by the
374 % CAPTION module from the list of supported formats.
375 %
376 % The format of the UnregisterCAPTIONImage method is:
377 %
378 % UnregisterCAPTIONImage(void)
379 %
380 */
UnregisterCAPTIONImage(void)381 ModuleExport void UnregisterCAPTIONImage(void)
382 {
383 (void) UnregisterMagickInfo("CAPTION");
384 }
385