/src/imagemagick/coders/caption.c
Line | Count | Source |
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 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 | | */ |
94 | | |
95 | | static inline void AdjustTypeMetricBounds(TypeMetric *metrics) |
96 | 33.1k | { |
97 | 33.1k | if (metrics->bounds.x1 >= 0.0) |
98 | 33.1k | metrics->bounds.x1=0.0; |
99 | 0 | else |
100 | 0 | { |
101 | 0 | double x1 = ceil(-metrics->bounds.x1+0.5); |
102 | 0 | metrics->width+=x1+x1; |
103 | 0 | metrics->bounds.x1=x1; |
104 | 0 | } |
105 | 33.1k | } |
106 | | |
107 | | static Image *ReadCAPTIONImage(const ImageInfo *image_info, |
108 | | ExceptionInfo *exception) |
109 | 25.7k | { |
110 | 25.7k | char |
111 | 25.7k | *caption, |
112 | 25.7k | geometry[MagickPathExtent], |
113 | 25.7k | *text; |
114 | | |
115 | 25.7k | const char |
116 | 25.7k | *gravity, |
117 | 25.7k | *option; |
118 | | |
119 | 25.7k | DrawInfo |
120 | 25.7k | *draw_info; |
121 | | |
122 | 25.7k | Image |
123 | 25.7k | *image; |
124 | | |
125 | 25.7k | MagickBooleanType |
126 | 25.7k | left_bearing, |
127 | 25.7k | split, |
128 | 25.7k | status; |
129 | | |
130 | 25.7k | size_t |
131 | 25.7k | height, |
132 | 25.7k | width; |
133 | | |
134 | 25.7k | ssize_t |
135 | 25.7k | i; |
136 | | |
137 | 25.7k | TypeMetric |
138 | 25.7k | metrics; |
139 | | |
140 | | /* |
141 | | Initialize Image structure. |
142 | | */ |
143 | 25.7k | assert(image_info != (const ImageInfo *) NULL); |
144 | 25.7k | assert(image_info->signature == MagickCoreSignature); |
145 | 25.7k | assert(exception != (ExceptionInfo *) NULL); |
146 | 25.7k | assert(exception->signature == MagickCoreSignature); |
147 | 25.7k | if (IsEventLogging() != MagickFalse) |
148 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
149 | 0 | image_info->filename); |
150 | 25.7k | image=AcquireImage(image_info,exception); |
151 | 25.7k | (void) ResetImagePage(image,"0x0+0+0"); |
152 | 25.7k | if ((image->columns != 0) && (image->rows != 0)) |
153 | 692 | (void) SetImageBackgroundColor(image,exception); |
154 | | /* |
155 | | Format caption. |
156 | | */ |
157 | 25.7k | option=GetImageOption(image_info,"filename"); |
158 | 25.7k | if (option == (const char *) NULL) |
159 | 25.7k | caption=InterpretImageProperties((ImageInfo *) image_info,image, |
160 | 25.7k | image_info->filename,exception); |
161 | 0 | else |
162 | 0 | if (LocaleNCompare(option,"caption:",8) == 0) |
163 | 0 | caption=InterpretImageProperties((ImageInfo *) image_info,image,option+8, |
164 | 0 | exception); |
165 | 0 | else |
166 | 0 | caption=InterpretImageProperties((ImageInfo *) image_info,image,option, |
167 | 0 | exception); |
168 | 25.7k | if (caption == (char *) NULL) |
169 | 9.62k | return(DestroyImageList(image)); |
170 | 16.1k | (void) SetImageProperty(image,"caption",caption,exception); |
171 | 16.1k | draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL); |
172 | 16.1k | width=CastDoubleToSizeT(0.5*draw_info->pointsize*strlen(caption)+0.5); |
173 | 16.1k | if (AcquireMagickResource(WidthResource,width) == MagickFalse) |
174 | 7.47k | { |
175 | 7.47k | caption=DestroyString(caption); |
176 | 7.47k | draw_info=DestroyDrawInfo(draw_info); |
177 | 7.47k | ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit"); |
178 | 0 | } |
179 | 8.70k | (void) CloneString(&draw_info->text,caption); |
180 | 8.70k | gravity=GetImageOption(image_info,"gravity"); |
181 | 8.70k | if (gravity != (char *) NULL) |
182 | 0 | draw_info->gravity=(GravityType) ParseCommandOption(MagickGravityOptions, |
183 | 0 | MagickFalse,gravity); |
184 | 8.70k | split=IsStringTrue(GetImageOption(image_info,"caption:split")); |
185 | 8.70k | status=MagickTrue; |
186 | 8.70k | (void) memset(&metrics,0,sizeof(metrics)); |
187 | 8.70k | if (image->columns == 0) |
188 | 7.81k | { |
189 | 7.81k | text=AcquireString(caption); |
190 | 7.81k | i=FormatMagickCaption(image,draw_info,split,&metrics,&text,exception); |
191 | 7.81k | AdjustTypeMetricBounds(&metrics); |
192 | 7.81k | (void) CloneString(&draw_info->text,text); |
193 | 7.81k | text=DestroyString(text); |
194 | 7.81k | (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g", |
195 | 7.81k | metrics.bounds.x1,metrics.ascent); |
196 | 7.81k | if (draw_info->gravity == UndefinedGravity) |
197 | 7.81k | (void) CloneString(&draw_info->geometry,geometry); |
198 | 7.81k | status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception); |
199 | 7.81k | AdjustTypeMetricBounds(&metrics); |
200 | 7.81k | image->columns=CastDoubleToSizeT(floor(metrics.width+draw_info->stroke_width+0.5)); |
201 | 7.81k | } |
202 | 8.70k | if (image->rows == 0) |
203 | 8.17k | { |
204 | 8.17k | split=MagickTrue; |
205 | 8.17k | text=AcquireString(caption); |
206 | 8.17k | i=FormatMagickCaption(image,draw_info,split,&metrics,&text,exception); |
207 | 8.17k | AdjustTypeMetricBounds(&metrics); |
208 | 8.17k | (void) CloneString(&draw_info->text,text); |
209 | 8.17k | text=DestroyString(text); |
210 | 8.17k | (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g", |
211 | 8.17k | metrics.bounds.x1,metrics.ascent); |
212 | 8.17k | if (draw_info->gravity == UndefinedGravity) |
213 | 8.17k | (void) CloneString(&draw_info->geometry,geometry); |
214 | 8.17k | status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception); |
215 | 8.17k | AdjustTypeMetricBounds(&metrics); |
216 | 8.17k | image->rows=CastDoubleToSizeT(((i+1)*(metrics.ascent-metrics.descent+ |
217 | 8.17k | draw_info->interline_spacing+draw_info->stroke_width)+0.5)); |
218 | 8.17k | } |
219 | 8.70k | if (status != MagickFalse) |
220 | 510 | status=SetImageExtent(image,image->columns,image->rows,exception); |
221 | 8.70k | if (status == MagickFalse) |
222 | 8.32k | { |
223 | 8.32k | caption=DestroyString(caption); |
224 | 8.32k | draw_info=DestroyDrawInfo(draw_info); |
225 | 8.32k | return(DestroyImageList(image)); |
226 | 8.32k | } |
227 | 381 | if (SetImageBackgroundColor(image,exception) == MagickFalse) |
228 | 0 | { |
229 | 0 | caption=DestroyString(caption); |
230 | 0 | draw_info=DestroyDrawInfo(draw_info); |
231 | 0 | image=DestroyImageList(image); |
232 | 0 | return((Image *) NULL); |
233 | 0 | } |
234 | 381 | if ((fabs(image_info->pointsize) < MagickEpsilon) && (strlen(caption) > 0)) |
235 | 381 | { |
236 | 381 | double |
237 | 381 | high, |
238 | 381 | low; |
239 | | |
240 | 381 | ssize_t |
241 | 381 | n; |
242 | | |
243 | | /* |
244 | | Auto fit text into bounding box. |
245 | | */ |
246 | 381 | low=1.0; |
247 | 381 | option=GetImageOption(image_info,"caption:max-pointsize"); |
248 | 381 | if (option != (const char*) NULL) |
249 | 0 | { |
250 | 0 | high=StringToDouble(option,(char**) NULL); |
251 | 0 | if (high < 1.0) |
252 | 0 | high=1.0; |
253 | 0 | high+=1.0; |
254 | 0 | } |
255 | 381 | else |
256 | 381 | { |
257 | 381 | option=GetImageOption(image_info,"caption:start-pointsize"); |
258 | 381 | if (option != (const char *) NULL) |
259 | 0 | { |
260 | 0 | draw_info->pointsize=StringToDouble(option,(char**) NULL); |
261 | 0 | if (draw_info->pointsize < 1.0) |
262 | 0 | draw_info->pointsize=1.0; |
263 | 0 | } |
264 | 381 | for (n=0; n < 32; n++, draw_info->pointsize*=2.0) |
265 | 381 | { |
266 | 381 | text=AcquireString(caption); |
267 | 381 | i=FormatMagickCaption(image,draw_info,split,&metrics,&text, |
268 | 381 | exception); |
269 | 381 | AdjustTypeMetricBounds(&metrics); |
270 | 381 | (void) CloneString(&draw_info->text,text); |
271 | 381 | text=DestroyString(text); |
272 | 381 | (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g", |
273 | 381 | metrics.bounds.x1,metrics.ascent); |
274 | 381 | if (draw_info->gravity == UndefinedGravity) |
275 | 381 | (void) CloneString(&draw_info->geometry,geometry); |
276 | 381 | status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception); |
277 | 381 | if (status == MagickFalse) |
278 | 381 | break; |
279 | 0 | AdjustTypeMetricBounds(&metrics); |
280 | 0 | width=CastDoubleToSizeT(metrics.width+draw_info->stroke_width+0.5); |
281 | 0 | height=CastDoubleToSizeT(metrics.height-metrics.underline_position+ |
282 | 0 | draw_info->interline_spacing+draw_info->stroke_width+0.5); |
283 | 0 | if ((image->columns != 0) && (image->rows != 0)) |
284 | 0 | { |
285 | 0 | if ((width > image->columns) && (height > image->rows)) |
286 | 0 | break; |
287 | 0 | if ((width <= image->columns) && (height <= image->rows)) |
288 | 0 | low=draw_info->pointsize; |
289 | 0 | } |
290 | 0 | else |
291 | 0 | if (((image->columns != 0) && (width > image->columns)) || |
292 | 0 | ((image->rows != 0) && (height > image->rows))) |
293 | 0 | break; |
294 | 0 | } |
295 | 381 | high=draw_info->pointsize; |
296 | 381 | } |
297 | 381 | while ((high-low) > 0.5) |
298 | 381 | { |
299 | 381 | draw_info->pointsize=(low+high)/2.0; |
300 | 381 | text=AcquireString(caption); |
301 | 381 | i=FormatMagickCaption(image,draw_info,split,&metrics,&text,exception); |
302 | 381 | AdjustTypeMetricBounds(&metrics); |
303 | 381 | (void) CloneString(&draw_info->text,text); |
304 | 381 | text=DestroyString(text); |
305 | 381 | (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g", |
306 | 381 | metrics.bounds.x1,metrics.ascent); |
307 | 381 | if (draw_info->gravity == UndefinedGravity) |
308 | 381 | (void) CloneString(&draw_info->geometry,geometry); |
309 | 381 | status=GetMultilineTypeMetrics(image,draw_info,&metrics,exception); |
310 | 381 | if (status == MagickFalse) |
311 | 381 | break; |
312 | 0 | AdjustTypeMetricBounds(&metrics); |
313 | 0 | width=CastDoubleToSizeT(metrics.width+draw_info->stroke_width+0.5); |
314 | 0 | height=CastDoubleToSizeT(metrics.height-metrics.underline_position+ |
315 | 0 | draw_info->interline_spacing+draw_info->stroke_width+0.5); |
316 | 0 | if ((image->columns != 0) && (image->rows != 0)) |
317 | 0 | { |
318 | 0 | if ((width <= image->columns) && (height <= image->rows)) |
319 | 0 | low=draw_info->pointsize+0.5; |
320 | 0 | else |
321 | 0 | high=draw_info->pointsize-0.5; |
322 | 0 | } |
323 | 0 | else |
324 | 0 | if (((image->columns != 0) && (width <= image->columns)) || |
325 | 0 | ((image->rows != 0) && (height <= image->rows))) |
326 | 0 | low=draw_info->pointsize+0.5; |
327 | 0 | else |
328 | 0 | high=draw_info->pointsize-0.5; |
329 | 0 | } |
330 | 381 | if (status != MagickFalse) |
331 | 0 | draw_info->pointsize=floor(low-0.5); |
332 | 381 | } |
333 | | /* |
334 | | Draw caption. |
335 | | */ |
336 | 381 | i=FormatMagickCaption(image,draw_info,split,&metrics,&caption,exception); |
337 | 381 | AdjustTypeMetricBounds(&metrics); |
338 | 381 | (void) CloneString(&draw_info->text,caption); |
339 | 381 | caption=DestroyString(caption); |
340 | 381 | left_bearing=((draw_info->gravity == UndefinedGravity) || |
341 | 0 | (draw_info->gravity == NorthWestGravity) || |
342 | 0 | (draw_info->gravity == WestGravity) || |
343 | 381 | (draw_info->gravity == SouthWestGravity)) ? MagickTrue : MagickFalse; |
344 | 381 | (void) FormatLocaleString(geometry,MagickPathExtent,"%+g%+g", |
345 | 381 | (draw_info->direction == RightToLeftDirection ? (double) image->columns- |
346 | 0 | (draw_info->gravity == UndefinedGravity ? metrics.bounds.x2 : 0.0) : |
347 | 381 | (left_bearing != MagickFalse ? metrics.bounds.x1 : 0.0)), |
348 | 381 | (draw_info->gravity == UndefinedGravity ? |
349 | 381 | MagickMax(metrics.ascent,metrics.bounds.y2) : 0.0)); |
350 | 381 | (void) CloneString(&draw_info->geometry,geometry); |
351 | 381 | status=AnnotateImage(image,draw_info,exception); |
352 | 381 | if (image_info->pointsize == 0.0) |
353 | 381 | (void) FormatImageProperty(image,"caption:pointsize","%.*g", |
354 | 381 | GetMagickPrecision(),draw_info->pointsize); |
355 | 381 | (void) FormatImageProperty(image,"caption:lines","%.*g",GetMagickPrecision(), |
356 | 381 | (double) (i+1)); |
357 | 381 | draw_info=DestroyDrawInfo(draw_info); |
358 | 381 | if (status == MagickFalse) |
359 | 371 | { |
360 | 371 | image=DestroyImageList(image); |
361 | 371 | return((Image *) NULL); |
362 | 371 | } |
363 | 10 | return(GetFirstImageInList(image)); |
364 | 381 | } |
365 | | |
366 | | /* |
367 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
368 | | % % |
369 | | % % |
370 | | % % |
371 | | % R e g i s t e r C A P T I O N I m a g e % |
372 | | % % |
373 | | % % |
374 | | % % |
375 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
376 | | % |
377 | | % RegisterCAPTIONImage() adds attributes for the CAPTION image format to |
378 | | % the list of supported formats. The attributes include the image format |
379 | | % tag, a method to read and/or write the format, whether the format |
380 | | % supports the saving of more than one frame to the same file or blob, |
381 | | % whether the format supports native in-memory I/O, and a brief |
382 | | % description of the format. |
383 | | % |
384 | | % The format of the RegisterCAPTIONImage method is: |
385 | | % |
386 | | % size_t RegisterCAPTIONImage(void) |
387 | | % |
388 | | */ |
389 | | ModuleExport size_t RegisterCAPTIONImage(void) |
390 | 7 | { |
391 | 7 | MagickInfo |
392 | 7 | *entry; |
393 | | |
394 | 7 | entry=AcquireMagickInfo("CAPTION","CAPTION","Caption"); |
395 | 7 | entry->decoder=(DecodeImageHandler *) ReadCAPTIONImage; |
396 | 7 | entry->flags^=CoderAdjoinFlag; |
397 | 7 | (void) RegisterMagickInfo(entry); |
398 | 7 | return(MagickImageCoderSignature); |
399 | 7 | } |
400 | | |
401 | | /* |
402 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
403 | | % % |
404 | | % % |
405 | | % % |
406 | | % U n r e g i s t e r C A P T I O N I m a g e % |
407 | | % % |
408 | | % % |
409 | | % % |
410 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
411 | | % |
412 | | % UnregisterCAPTIONImage() removes format registrations made by the |
413 | | % CAPTION module from the list of supported formats. |
414 | | % |
415 | | % The format of the UnregisterCAPTIONImage method is: |
416 | | % |
417 | | % UnregisterCAPTIONImage(void) |
418 | | % |
419 | | */ |
420 | | ModuleExport void UnregisterCAPTIONImage(void) |
421 | 0 | { |
422 | 0 | (void) UnregisterMagickInfo("CAPTION"); |
423 | 0 | } |