1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % AAA N N N N OOO TTTTT AAA TTTTT EEEEE %
7 % A A NN N NN N O O T A A T E %
8 % AAAAA N N N N N N O O T AAAAA T EEE %
9 % A A N NN N NN O O T A A T E %
10 % A A N N N N OOO T A A T EEEEE %
11 % %
12 % %
13 % MagickCore Image Annotation Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
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 % Digital Applications (www.digapp.com) contributed the stroked text algorithm.
37 % It was written by Leonard Rosenthol.
38 %
39 %
40 */
41
42 /*
43 Include declarations.
44 */
45 #include "MagickCore/studio.h"
46 #include "MagickCore/annotate.h"
47 #include "MagickCore/annotate-private.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/cache-private.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/composite.h"
57 #include "MagickCore/composite-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/draw-private.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/gem.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/log.h"
68 #include "MagickCore/quantum.h"
69 #include "MagickCore/quantum-private.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/policy.h"
72 #include "MagickCore/property.h"
73 #include "MagickCore/resource_.h"
74 #include "MagickCore/semaphore.h"
75 #include "MagickCore/statistic.h"
76 #include "MagickCore/string_.h"
77 #include "MagickCore/token.h"
78 #include "MagickCore/token-private.h"
79 #include "MagickCore/transform.h"
80 #include "MagickCore/transform-private.h"
81 #include "MagickCore/type.h"
82 #include "MagickCore/utility.h"
83 #include "MagickCore/utility-private.h"
84 #include "MagickCore/xwindow.h"
85 #include "MagickCore/xwindow-private.h"
86 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
87 #if defined(__MINGW32__)
88 # undef interface
89 #endif
90 #include <ft2build.h>
91 #if defined(FT_FREETYPE_H)
92 # include FT_FREETYPE_H
93 #else
94 # include <freetype/freetype.h>
95 #endif
96 #if defined(FT_GLYPH_H)
97 # include FT_GLYPH_H
98 #else
99 # include <freetype/ftglyph.h>
100 #endif
101 #if defined(FT_OUTLINE_H)
102 # include FT_OUTLINE_H
103 #else
104 # include <freetype/ftoutln.h>
105 #endif
106 #if defined(FT_BBOX_H)
107 # include FT_BBOX_H
108 #else
109 # include <freetype/ftbbox.h>
110 #endif /* defined(FT_BBOX_H) */
111 #endif
112 #if defined(MAGICKCORE_RAQM_DELEGATE)
113 #include <raqm.h>
114 #endif
115 typedef struct _GraphemeInfo
116 {
117 size_t
118 index,
119 x_offset,
120 x_advance,
121 y_offset;
122
123 size_t
124 cluster;
125 } GraphemeInfo;
126
127 /*
128 Annotate semaphores.
129 */
130 static SemaphoreInfo
131 *annotate_semaphore = (SemaphoreInfo *) NULL;
132
133 /*
134 Forward declarations.
135 */
136 static MagickBooleanType
137 RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
138 ExceptionInfo *),
139 RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
140 ExceptionInfo *),
141 RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
142 TypeMetric *,ExceptionInfo *),
143 RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
144 ExceptionInfo *);
145
146 /*
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 % %
149 % %
150 % %
151 + A n n o t a t e C o m p o n e n t G e n e s i s %
152 % %
153 % %
154 % %
155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156 %
157 % AnnotateComponentGenesis() instantiates the annotate component.
158 %
159 % The format of the AnnotateComponentGenesis method is:
160 %
161 % MagickBooleanType AnnotateComponentGenesis(void)
162 %
163 */
AnnotateComponentGenesis(void)164 MagickPrivate MagickBooleanType AnnotateComponentGenesis(void)
165 {
166 if (annotate_semaphore == (SemaphoreInfo *) NULL)
167 annotate_semaphore=AcquireSemaphoreInfo();
168 return(MagickTrue);
169 }
170
171 /*
172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
173 % %
174 % %
175 % %
176 + A n n o t a t e C o m p o n e n t T e r m i n u s %
177 % %
178 % %
179 % %
180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181 %
182 % AnnotateComponentTerminus() destroys the annotate component.
183 %
184 % The format of the AnnotateComponentTerminus method is:
185 %
186 % AnnotateComponentTerminus(void)
187 %
188 */
AnnotateComponentTerminus(void)189 MagickPrivate void AnnotateComponentTerminus(void)
190 {
191 if (annotate_semaphore == (SemaphoreInfo *) NULL)
192 ActivateSemaphoreInfo(&annotate_semaphore);
193 RelinquishSemaphoreInfo(&annotate_semaphore);
194 }
195
196 /*
197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
198 % %
199 % %
200 % %
201 % A n n o t a t e I m a g e %
202 % %
203 % %
204 % %
205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
206 %
207 % AnnotateImage() annotates an image with text.
208 %
209 % The format of the AnnotateImage method is:
210 %
211 % MagickBooleanType AnnotateImage(Image *image,DrawInfo *draw_info,
212 % ExceptionInfo *exception)
213 %
214 % A description of each parameter follows:
215 %
216 % o image: the image.
217 %
218 % o draw_info: the draw info.
219 %
220 % o exception: return any errors or warnings in this structure.
221 %
222 */
AnnotateImage(Image * image,const DrawInfo * draw_info,ExceptionInfo * exception)223 MagickExport MagickBooleanType AnnotateImage(Image *image,
224 const DrawInfo *draw_info,ExceptionInfo *exception)
225 {
226 char
227 *p,
228 color[MagickPathExtent],
229 primitive[MagickPathExtent],
230 *text,
231 **textlist;
232
233 DrawInfo
234 *annotate,
235 *annotate_info;
236
237 GeometryInfo
238 geometry_info;
239
240 MagickBooleanType
241 status;
242
243 PixelInfo
244 pixel;
245
246 PointInfo
247 offset;
248
249 RectangleInfo
250 geometry;
251
252 ssize_t
253 i;
254
255 TypeMetric
256 metrics;
257
258 size_t
259 height,
260 number_lines;
261
262 assert(image != (Image *) NULL);
263 assert(image->signature == MagickCoreSignature);
264 if (image->debug != MagickFalse)
265 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
266 assert(draw_info != (DrawInfo *) NULL);
267 assert(draw_info->signature == MagickCoreSignature);
268 if (draw_info->text == (char *) NULL)
269 return(MagickFalse);
270 if (*draw_info->text == '\0')
271 return(MagickTrue);
272 annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
273 text=annotate->text;
274 annotate->text=(char *) NULL;
275 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
276 number_lines=1;
277 for (p=text; *p != '\0'; p++)
278 if (*p == '\n')
279 number_lines++;
280 textlist=(char **) AcquireQuantumMemory(number_lines+1,sizeof(*textlist));
281 if (textlist == (char **) NULL)
282 {
283 annotate_info=DestroyDrawInfo(annotate_info);
284 annotate=DestroyDrawInfo(annotate);
285 text=DestroyString(text);
286 return(MagickFalse);
287 }
288 p=text;
289 for (i=0; i < (ssize_t) number_lines; i++)
290 {
291 char
292 *q;
293
294 textlist[i]=p;
295 for (q=p; *q != '\0'; q++)
296 if ((*q == '\r') || (*q == '\n'))
297 break;
298 if (*q == '\r')
299 {
300 *q='\0';
301 q++;
302 }
303 *q='\0';
304 p=q+1;
305 }
306 textlist[i]=(char *) NULL;
307 SetGeometry(image,&geometry);
308 SetGeometryInfo(&geometry_info);
309 if (annotate_info->geometry != (char *) NULL)
310 {
311 (void) ParsePageGeometry(image,annotate_info->geometry,&geometry,
312 exception);
313 (void) ParseGeometry(annotate_info->geometry,&geometry_info);
314 }
315 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
316 {
317 annotate_info=DestroyDrawInfo(annotate_info);
318 annotate=DestroyDrawInfo(annotate);
319 textlist=(char **) RelinquishMagickMemory(textlist);
320 text=DestroyString(text);
321 return(MagickFalse);
322 }
323 if (IsGrayColorspace(image->colorspace) != MagickFalse)
324 (void) SetImageColorspace(image,sRGBColorspace,exception);
325 status=MagickTrue;
326 (void) memset(&metrics,0,sizeof(metrics));
327 for (i=0; textlist[i] != (char *) NULL; i++)
328 {
329 if (*textlist[i] == '\0')
330 continue;
331 /*
332 Position text relative to image.
333 */
334 annotate_info->affine.tx=geometry_info.xi-image->page.x;
335 annotate_info->affine.ty=geometry_info.psi-image->page.y;
336 (void) CloneString(&annotate->text,textlist[i]);
337 if ((metrics.width == 0) || (annotate->gravity != NorthWestGravity))
338 (void) GetTypeMetrics(image,annotate,&metrics,exception);
339 height=(size_t) floor(metrics.ascent-metrics.descent+0.5);
340 if (height == 0)
341 height=draw_info->pointsize;
342 height+=(size_t) floor(draw_info->interline_spacing+0.5);
343 switch (annotate->gravity)
344 {
345 case UndefinedGravity:
346 default:
347 {
348 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
349 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
350 break;
351 }
352 case NorthWestGravity:
353 {
354 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
355 annotate_info->affine.ry*height+annotate_info->affine.ry*
356 (metrics.ascent+metrics.descent);
357 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
358 annotate_info->affine.sy*height+annotate_info->affine.sy*
359 metrics.ascent;
360 break;
361 }
362 case NorthGravity:
363 {
364 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
365 geometry.width/2.0+i*annotate_info->affine.ry*height-
366 annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
367 (metrics.ascent+metrics.descent);
368 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
369 annotate_info->affine.sy*height+annotate_info->affine.sy*
370 metrics.ascent-annotate_info->affine.rx*metrics.width/2.0;
371 break;
372 }
373 case NorthEastGravity:
374 {
375 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
376 geometry.width+i*annotate_info->affine.ry*height-
377 annotate_info->affine.sx*metrics.width+annotate_info->affine.ry*
378 (metrics.ascent+metrics.descent)-1.0;
379 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
380 annotate_info->affine.sy*height+annotate_info->affine.sy*
381 metrics.ascent-annotate_info->affine.rx*metrics.width;
382 break;
383 }
384 case WestGravity:
385 {
386 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
387 annotate_info->affine.ry*height+annotate_info->affine.ry*
388 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
389 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
390 geometry.height/2.0+i*annotate_info->affine.sy*height+
391 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
392 (number_lines-1.0)*height)/2.0;
393 break;
394 }
395 case CenterGravity:
396 {
397 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
398 geometry.width/2.0+i*annotate_info->affine.ry*height-
399 annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
400 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
401 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
402 geometry.height/2.0+i*annotate_info->affine.sy*height-
403 annotate_info->affine.rx*metrics.width/2.0+annotate_info->affine.sy*
404 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
405 break;
406 }
407 case EastGravity:
408 {
409 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
410 geometry.width+i*annotate_info->affine.ry*height-
411 annotate_info->affine.sx*metrics.width+
412 annotate_info->affine.ry*(metrics.ascent+metrics.descent-
413 (number_lines-1.0)*height)/2.0-1.0;
414 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
415 geometry.height/2.0+i*annotate_info->affine.sy*height-
416 annotate_info->affine.rx*metrics.width+
417 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
418 (number_lines-1.0)*height)/2.0;
419 break;
420 }
421 case SouthWestGravity:
422 {
423 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
424 annotate_info->affine.ry*height-annotate_info->affine.ry*
425 (number_lines-1.0)*height;
426 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
427 geometry.height+i*annotate_info->affine.sy*height-
428 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
429 break;
430 }
431 case SouthGravity:
432 {
433 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
434 geometry.width/2.0+i*annotate_info->affine.ry*height-
435 annotate_info->affine.sx*metrics.width/2.0-
436 annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
437 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
438 geometry.height+i*annotate_info->affine.sy*height-
439 annotate_info->affine.rx*metrics.width/2.0-
440 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
441 break;
442 }
443 case SouthEastGravity:
444 {
445 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
446 geometry.width+i*annotate_info->affine.ry*height-
447 annotate_info->affine.sx*metrics.width-
448 annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
449 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
450 geometry.height+i*annotate_info->affine.sy*height-
451 annotate_info->affine.rx*metrics.width-
452 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
453 break;
454 }
455 }
456 switch (annotate->align)
457 {
458 case LeftAlign:
459 {
460 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
461 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
462 break;
463 }
464 case CenterAlign:
465 {
466 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
467 annotate_info->affine.sx*metrics.width/2.0;
468 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
469 annotate_info->affine.rx*metrics.width/2.0;
470 break;
471 }
472 case RightAlign:
473 {
474 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
475 annotate_info->affine.sx*metrics.width;
476 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
477 annotate_info->affine.rx*metrics.width;
478 break;
479 }
480 default:
481 break;
482 }
483 if (draw_info->undercolor.alpha != TransparentAlpha)
484 {
485 DrawInfo
486 *undercolor_info;
487
488 /*
489 Text box.
490 */
491 undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
492 undercolor_info->fill=draw_info->undercolor;
493 undercolor_info->affine=draw_info->affine;
494 undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
495 undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
496 (void) FormatLocaleString(primitive,MagickPathExtent,
497 "rectangle 0.0,0.0 %g,%g",metrics.origin.x,(double) height);
498 (void) CloneString(&undercolor_info->primitive,primitive);
499 (void) DrawImage(image,undercolor_info,exception);
500 (void) DestroyDrawInfo(undercolor_info);
501 }
502 annotate_info->affine.tx=offset.x;
503 annotate_info->affine.ty=offset.y;
504 pixel=annotate_info->fill;
505 if (annotate_info->stroke.alpha != TransparentAlpha)
506 pixel=annotate_info->stroke;
507 (void) QueryColorname(image,&pixel,AllCompliance,color,exception);
508 (void) FormatLocaleString(primitive,MagickPathExtent,"stroke %s "
509 "stroke-width %g line 0,0 %g,0",color,(double)
510 metrics.underline_thickness,(double) metrics.width);
511 /*
512 Annotate image with text.
513 */
514 switch (annotate->decorate)
515 {
516 case OverlineDecoration:
517 {
518 annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
519 metrics.descent-metrics.underline_position));
520 (void) CloneString(&annotate_info->primitive,primitive);
521 (void) DrawImage(image,annotate_info,exception);
522 break;
523 }
524 case UnderlineDecoration:
525 {
526 annotate_info->affine.ty-=(draw_info->affine.sy*
527 metrics.underline_position);
528 (void) CloneString(&annotate_info->primitive,primitive);
529 (void) DrawImage(image,annotate_info,exception);
530 break;
531 }
532 default:
533 break;
534 }
535 status=RenderType(image,annotate,&offset,&metrics,exception);
536 if (status == MagickFalse)
537 break;
538
539 if (annotate->decorate == LineThroughDecoration) {
540 annotate_info->affine.ty-=(draw_info->affine.sy*(height+
541 metrics.underline_position+metrics.descent*2)/2.0);
542 (void) CloneString(&annotate_info->primitive,primitive);
543 (void) DrawImage(image,annotate_info,exception);
544 }
545 }
546 /*
547 Relinquish resources.
548 */
549 annotate_info=DestroyDrawInfo(annotate_info);
550 annotate=DestroyDrawInfo(annotate);
551 textlist=(char **) RelinquishMagickMemory(textlist);
552 text=DestroyString(text);
553 return(status);
554 }
555
556 /*
557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558 % %
559 % %
560 % %
561 % F o r m a t M a g i c k C a p t i o n %
562 % %
563 % %
564 % %
565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566 %
567 % FormatMagickCaption() formats a caption so that it fits within the image
568 % width. It returns the number of lines in the formatted caption.
569 %
570 % The format of the FormatMagickCaption method is:
571 %
572 % ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
573 % const MagickBooleanType split,TypeMetric *metrics,char **caption,
574 % ExceptionInfo *exception)
575 %
576 % A description of each parameter follows.
577 %
578 % o image: The image.
579 %
580 % o draw_info: the draw info.
581 %
582 % o split: when no convenient line breaks-- insert newline.
583 %
584 % o metrics: Return the font metrics in this structure.
585 %
586 % o caption: the caption.
587 %
588 % o exception: return any errors or warnings in this structure.
589 %
590 */
FormatMagickCaption(Image * image,DrawInfo * draw_info,const MagickBooleanType split,TypeMetric * metrics,char ** caption,ExceptionInfo * exception)591 MagickExport ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
592 const MagickBooleanType split,TypeMetric *metrics,char **caption,
593 ExceptionInfo *exception)
594 {
595 MagickBooleanType
596 status;
597
598 char
599 *p,
600 *q,
601 *s;
602
603 ssize_t
604 i;
605
606 size_t
607 width;
608
609 ssize_t
610 n;
611
612 q=draw_info->text;
613 s=(char *) NULL;
614 for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
615 {
616 if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
617 s=p;
618 if (GetUTFCode(p) == '\n')
619 {
620 q=draw_info->text;
621 continue;
622 }
623 for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
624 *q++=(*(p+i));
625 *q='\0';
626 status=GetTypeMetrics(image,draw_info,metrics,exception);
627 if (status == MagickFalse)
628 break;
629 width=(size_t) floor(metrics->width+draw_info->stroke_width+0.5);
630 if (width <= image->columns)
631 continue;
632 if (s != (char *) NULL)
633 {
634 for (i=0; i < (ssize_t) GetUTFOctets(s); i++)
635 *(s+i)=' ';
636 *s='\n';
637 p=s;
638 }
639 else
640 if ((split != MagickFalse) || (GetUTFOctets(p) > 2))
641 {
642 /*
643 No convenient line breaks-- insert newline.
644 */
645 n=p-(*caption);
646 if ((n > 0) && ((*caption)[n-1] != '\n'))
647 {
648 char
649 *target;
650
651 target=AcquireString(*caption);
652 CopyMagickString(target,*caption,n+1);
653 ConcatenateMagickString(target,"\n",strlen(*caption)+1);
654 ConcatenateMagickString(target,p,strlen(*caption)+2);
655 (void) DestroyString(*caption);
656 *caption=target;
657 p=(*caption)+n;
658 }
659 }
660 q=draw_info->text;
661 s=(char *) NULL;
662 }
663 n=0;
664 for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
665 if (GetUTFCode(p) == '\n')
666 n++;
667 return(n);
668 }
669
670 /*
671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 % %
673 % %
674 % %
675 % G e t M u l t i l i n e T y p e M e t r i c s %
676 % %
677 % %
678 % %
679 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
680 %
681 % GetMultilineTypeMetrics() returns the following information for the
682 % specified font and text:
683 %
684 % character width
685 % character height
686 % ascender
687 % descender
688 % text width
689 % text height
690 % maximum horizontal advance
691 % bounds: x1
692 % bounds: y1
693 % bounds: x2
694 % bounds: y2
695 % origin: x
696 % origin: y
697 % underline position
698 % underline thickness
699 %
700 % This method is like GetTypeMetrics() but it returns the maximum text width
701 % and height for multiple lines of text.
702 %
703 % The format of the GetMultilineTypeMetrics method is:
704 %
705 % MagickBooleanType GetMultilineTypeMetrics(Image *image,
706 % const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
707 %
708 % A description of each parameter follows:
709 %
710 % o image: the image.
711 %
712 % o draw_info: the draw info.
713 %
714 % o metrics: Return the font metrics in this structure.
715 %
716 % o exception: return any errors or warnings in this structure.
717 %
718 */
GetMultilineTypeMetrics(Image * image,const DrawInfo * draw_info,TypeMetric * metrics,ExceptionInfo * exception)719 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
720 const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
721 {
722 char
723 **textlist;
724
725 double
726 height;
727
728 DrawInfo
729 *annotate_info;
730
731 MagickBooleanType
732 status;
733
734 MagickSizeType
735 size;
736
737 ssize_t
738 i;
739
740 size_t
741 count;
742
743 TypeMetric
744 extent;
745
746 assert(image != (Image *) NULL);
747 assert(image->signature == MagickCoreSignature);
748 if (image->debug != MagickFalse)
749 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
750 assert(draw_info != (DrawInfo *) NULL);
751 assert(draw_info->text != (char *) NULL);
752 assert(draw_info->signature == MagickCoreSignature);
753 if (*draw_info->text == '\0')
754 return(MagickFalse);
755 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
756 annotate_info->text=DestroyString(annotate_info->text);
757 /*
758 Convert newlines to multiple lines of text.
759 */
760 textlist=StringToStrings(draw_info->text,&count);
761 if (textlist == (char **) NULL)
762 return(MagickFalse);
763 annotate_info->render=MagickFalse;
764 annotate_info->direction=UndefinedDirection;
765 (void) memset(metrics,0,sizeof(*metrics));
766 (void) memset(&extent,0,sizeof(extent));
767 /*
768 Find the widest of the text lines.
769 */
770 annotate_info->text=textlist[0];
771 status=GetTypeMetrics(image,annotate_info,&extent,exception);
772 *metrics=extent;
773 height=(count*(size_t) (metrics->ascent-metrics->descent+
774 0.5)+(count-1)*draw_info->interline_spacing);
775 size=(MagickSizeType) fabs(height);
776 if (AcquireMagickResource(HeightResource,size) == MagickFalse)
777 {
778 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
779 "WidthOrHeightExceedsLimit","`%s'",image->filename);
780 status=MagickFalse;
781 }
782 else
783 {
784 for (i=1; i < (ssize_t) count; i++)
785 {
786 annotate_info->text=textlist[i];
787 status=GetTypeMetrics(image,annotate_info,&extent,exception);
788 if (status == MagickFalse)
789 break;
790 if (extent.width > metrics->width)
791 *metrics=extent;
792 size=(MagickSizeType) fabs(extent.width);
793 if (AcquireMagickResource(WidthResource,size) == MagickFalse)
794 {
795 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
796 "WidthOrHeightExceedsLimit","`%s'",image->filename);
797 status=MagickFalse;
798 break;
799 }
800 }
801 metrics->height=(double) height;
802 }
803 /*
804 Relinquish resources.
805 */
806 annotate_info->text=(char *) NULL;
807 annotate_info=DestroyDrawInfo(annotate_info);
808 for (i=0; i < (ssize_t) count; i++)
809 textlist[i]=DestroyString(textlist[i]);
810 textlist=(char **) RelinquishMagickMemory(textlist);
811 return(status);
812 }
813
814 /*
815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816 % %
817 % %
818 % %
819 % G e t T y p e M e t r i c s %
820 % %
821 % %
822 % %
823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824 %
825 % GetTypeMetrics() returns the following information for the specified font
826 % and text:
827 %
828 % character width
829 % character height
830 % ascender
831 % descender
832 % text width
833 % text height
834 % maximum horizontal advance
835 % bounds: x1
836 % bounds: y1
837 % bounds: x2
838 % bounds: y2
839 % origin: x
840 % origin: y
841 % underline position
842 % underline thickness
843 %
844 % The format of the GetTypeMetrics method is:
845 %
846 % MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
847 % TypeMetric *metrics,ExceptionInfo *exception)
848 %
849 % A description of each parameter follows:
850 %
851 % o image: the image.
852 %
853 % o draw_info: the draw info.
854 %
855 % o metrics: Return the font metrics in this structure.
856 %
857 % o exception: return any errors or warnings in this structure.
858 %
859 */
GetTypeMetrics(Image * image,const DrawInfo * draw_info,TypeMetric * metrics,ExceptionInfo * exception)860 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
861 const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
862 {
863 DrawInfo
864 *annotate_info;
865
866 MagickBooleanType
867 status;
868
869 PointInfo
870 offset;
871
872 assert(image != (Image *) NULL);
873 assert(image->signature == MagickCoreSignature);
874 if (image->debug != MagickFalse)
875 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
876 assert(draw_info != (DrawInfo *) NULL);
877 assert(draw_info->text != (char *) NULL);
878 assert(draw_info->signature == MagickCoreSignature);
879 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
880 annotate_info->render=MagickFalse;
881 annotate_info->direction=UndefinedDirection;
882 (void) memset(metrics,0,sizeof(*metrics));
883 offset.x=0.0;
884 offset.y=0.0;
885 status=RenderType(image,annotate_info,&offset,metrics,exception);
886 if (image->debug != MagickFalse)
887 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
888 "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
889 "bounds: %g,%g %g,%g; origin: %g,%g; pixels per em: %g,%g; "
890 "underline position: %g; underline thickness: %g",annotate_info->text,
891 metrics->width,metrics->height,metrics->ascent,metrics->descent,
892 metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
893 metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
894 metrics->pixels_per_em.x,metrics->pixels_per_em.y,
895 metrics->underline_position,metrics->underline_thickness);
896 annotate_info=DestroyDrawInfo(annotate_info);
897 return(status);
898 }
899
900 /*
901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
902 % %
903 % %
904 % %
905 + R e n d e r T y p e %
906 % %
907 % %
908 % %
909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910 %
911 % RenderType() renders text on the image. It also returns the bounding box of
912 % the text relative to the image.
913 %
914 % The format of the RenderType method is:
915 %
916 % MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
917 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
918 %
919 % A description of each parameter follows:
920 %
921 % o image: the image.
922 %
923 % o draw_info: the draw info.
924 %
925 % o offset: (x,y) location of text relative to image.
926 %
927 % o metrics: bounding box of text.
928 %
929 % o exception: return any errors or warnings in this structure.
930 %
931 */
RenderType(Image * image,const DrawInfo * draw_info,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)932 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
933 const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
934 {
935 char
936 *font;
937
938 const TypeInfo
939 *type_info;
940
941 DrawInfo
942 *annotate_info;
943
944 ExceptionInfo
945 *sans_exception;
946
947 MagickBooleanType
948 status;
949
950 type_info=(const TypeInfo *) NULL;
951 if (draw_info->font != (char *) NULL)
952 {
953 if (*draw_info->font == '@')
954 {
955 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
956 metrics,exception);
957 return(status);
958 }
959 if (*draw_info->font == '-')
960 return(RenderX11(image,draw_info,offset,metrics,exception));
961 if (*draw_info->font == '^')
962 return(RenderPostscript(image,draw_info,offset,metrics,exception));
963 if (IsPathAccessible(draw_info->font) != MagickFalse)
964 {
965 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
966 metrics,exception);
967 return(status);
968 }
969 type_info=GetTypeInfo(draw_info->font,exception);
970 if (type_info == (const TypeInfo *) NULL)
971 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
972 "UnableToReadFont","`%s'",draw_info->font);
973 }
974 if ((type_info == (const TypeInfo *) NULL) &&
975 (draw_info->family != (const char *) NULL))
976 {
977 if (strchr(draw_info->family,',') == (char *) NULL)
978 type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
979 draw_info->stretch,draw_info->weight,exception);
980 if (type_info == (const TypeInfo *) NULL)
981 {
982 char
983 **family;
984
985 int
986 number_families;
987
988 ssize_t
989 i;
990
991 /*
992 Parse font family list.
993 */
994 family=StringToArgv(draw_info->family,&number_families);
995 for (i=1; i < (ssize_t) number_families; i++)
996 {
997 type_info=GetTypeInfoByFamily(family[i],draw_info->style,
998 draw_info->stretch,draw_info->weight,exception);
999 if ((type_info != (const TypeInfo *) NULL) &&
1000 (LocaleCompare(family[i],type_info->family) == 0))
1001 break;
1002 }
1003 for (i=0; i < (ssize_t) number_families; i++)
1004 family[i]=DestroyString(family[i]);
1005 family=(char **) RelinquishMagickMemory(family);
1006 if (type_info == (const TypeInfo *) NULL)
1007 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1008 "UnableToReadFont","`%s'",draw_info->family);
1009 }
1010 }
1011 font=GetPolicyValue("system:font");
1012 if (font != (char *) NULL)
1013 {
1014 if (IsPathAccessible(font) != MagickFalse)
1015 {
1016 /*
1017 Render with default system font.
1018 */
1019 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1020 annotate_info->font=font;
1021 status=RenderFreetype(image,annotate_info,annotate_info->encoding,
1022 offset,metrics,exception);
1023 annotate_info=DestroyDrawInfo(annotate_info);
1024 return(status);
1025 }
1026 font=DestroyString(font);
1027 }
1028 sans_exception=AcquireExceptionInfo();
1029 if (type_info == (const TypeInfo *) NULL)
1030 type_info=GetTypeInfoByFamily("Open Sans",draw_info->style,
1031 draw_info->stretch,draw_info->weight,sans_exception);
1032 if (type_info == (const TypeInfo *) NULL)
1033 type_info=GetTypeInfoByFamily("Sans Serif",draw_info->style,
1034 draw_info->stretch,draw_info->weight,sans_exception);
1035 if (type_info == (const TypeInfo *) NULL)
1036 type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
1037 draw_info->stretch,draw_info->weight,sans_exception);
1038 if (type_info == (const TypeInfo *) NULL)
1039 type_info=GetTypeInfo("*",sans_exception);
1040 sans_exception=DestroyExceptionInfo(sans_exception);
1041 if (type_info == (const TypeInfo *) NULL)
1042 {
1043 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics,
1044 exception);
1045 return(status);
1046 }
1047 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1048 annotate_info->face=type_info->face;
1049 if (type_info->metrics != (char *) NULL)
1050 (void) CloneString(&annotate_info->metrics,type_info->metrics);
1051 if (type_info->glyphs != (char *) NULL)
1052 (void) CloneString(&annotate_info->font,type_info->glyphs);
1053 status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics,
1054 exception);
1055 annotate_info=DestroyDrawInfo(annotate_info);
1056 return(status);
1057 }
1058
1059 /*
1060 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061 % %
1062 % %
1063 % %
1064 + R e n d e r F r e e t y p e %
1065 % %
1066 % %
1067 % %
1068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069 %
1070 % RenderFreetype() renders text on the image with a Truetype font. It also
1071 % returns the bounding box of the text relative to the image.
1072 %
1073 % The format of the RenderFreetype method is:
1074 %
1075 % MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
1076 % const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1077 % ExceptionInfo *exception)
1078 %
1079 % A description of each parameter follows:
1080 %
1081 % o image: the image.
1082 %
1083 % o draw_info: the draw info.
1084 %
1085 % o encoding: the font encoding.
1086 %
1087 % o offset: (x,y) location of text relative to image.
1088 %
1089 % o metrics: bounding box of text.
1090 %
1091 % o exception: return any errors or warnings in this structure.
1092 %
1093 */
1094
1095 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
1096
ComplexTextLayout(const Image * image,const DrawInfo * draw_info,const char * text,const size_t length,const FT_Face face,const FT_Int32 flags,GraphemeInfo ** grapheme,ExceptionInfo * exception)1097 static size_t ComplexTextLayout(const Image *image,const DrawInfo *draw_info,
1098 const char *text,const size_t length,const FT_Face face,const FT_Int32 flags,
1099 GraphemeInfo **grapheme,ExceptionInfo *exception)
1100 {
1101 #if defined(MAGICKCORE_RAQM_DELEGATE)
1102 const char
1103 *features;
1104
1105 raqm_t
1106 *rq;
1107
1108 raqm_glyph_t
1109 *glyphs;
1110
1111 ssize_t
1112 i;
1113
1114 size_t
1115 extent;
1116
1117 magick_unreferenced(flags);
1118 extent=0;
1119 rq=raqm_create();
1120 if (rq == (raqm_t *) NULL)
1121 goto cleanup;
1122 if (raqm_set_text_utf8(rq,text,length) == 0)
1123 goto cleanup;
1124 if (raqm_set_par_direction(rq,(raqm_direction_t) draw_info->direction) == 0)
1125 goto cleanup;
1126 if (raqm_set_freetype_face(rq,face) == 0)
1127 goto cleanup;
1128 features=GetImageProperty(image,"type:features",exception);
1129 if (features != (const char *) NULL)
1130 {
1131 char
1132 breaker,
1133 quote,
1134 *token;
1135
1136 int
1137 next,
1138 status_token;
1139
1140 TokenInfo
1141 *token_info;
1142
1143 next=0;
1144 token_info=AcquireTokenInfo();
1145 token=AcquireString("");
1146 status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1147 &breaker,&next,"e);
1148 while (status_token == 0)
1149 {
1150 raqm_add_font_feature(rq,token,strlen(token));
1151 status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1152 &breaker,&next,"e);
1153 }
1154 token_info=DestroyTokenInfo(token_info);
1155 token=DestroyString(token);
1156 }
1157 if (raqm_layout(rq) == 0)
1158 goto cleanup;
1159 glyphs=raqm_get_glyphs(rq,&extent);
1160 if (glyphs == (raqm_glyph_t *) NULL)
1161 {
1162 extent=0;
1163 goto cleanup;
1164 }
1165 *grapheme=(GraphemeInfo *) AcquireQuantumMemory(extent,sizeof(**grapheme));
1166 if (*grapheme == (GraphemeInfo *) NULL)
1167 {
1168 extent=0;
1169 goto cleanup;
1170 }
1171 for (i=0; i < (ssize_t) extent; i++)
1172 {
1173 (*grapheme)[i].index=glyphs[i].index;
1174 (*grapheme)[i].x_offset=glyphs[i].x_offset;
1175 (*grapheme)[i].x_advance=glyphs[i].x_advance;
1176 (*grapheme)[i].y_offset=glyphs[i].y_offset;
1177 (*grapheme)[i].cluster=glyphs[i].cluster;
1178 }
1179
1180 cleanup:
1181 raqm_destroy(rq);
1182 return(extent);
1183 #else
1184 const char
1185 *p;
1186
1187 FT_Error
1188 ft_status;
1189
1190 ssize_t
1191 i;
1192
1193 ssize_t
1194 last_glyph;
1195
1196 /*
1197 Simple layout for bi-directional text (right-to-left or left-to-right).
1198 */
1199 magick_unreferenced(image);
1200 magick_unreferenced(exception);
1201 *grapheme=(GraphemeInfo *) AcquireQuantumMemory(length+1,sizeof(**grapheme));
1202 if (*grapheme == (GraphemeInfo *) NULL)
1203 return(0);
1204 last_glyph=0;
1205 p=text;
1206 for (i=0; GetUTFCode(p) != 0; p+=GetUTFOctets(p), i++)
1207 {
1208 (*grapheme)[i].index=(ssize_t) FT_Get_Char_Index(face,GetUTFCode(p));
1209 (*grapheme)[i].x_offset=0;
1210 (*grapheme)[i].y_offset=0;
1211 if (((*grapheme)[i].index != 0) && (last_glyph != 0))
1212 {
1213 if (FT_HAS_KERNING(face))
1214 {
1215 FT_Vector
1216 kerning;
1217
1218 ft_status=FT_Get_Kerning(face,(FT_UInt) last_glyph,(FT_UInt)
1219 (*grapheme)[i].index,ft_kerning_default,&kerning);
1220 if (ft_status == 0)
1221 (*grapheme)[i-1].x_advance+=(FT_Pos) ((draw_info->direction ==
1222 RightToLeftDirection ? -1.0 : 1.0)*kerning.x);
1223 }
1224 }
1225 ft_status=FT_Load_Glyph(face,(FT_UInt) (*grapheme)[i].index,flags);
1226 (*grapheme)[i].x_advance=face->glyph->advance.x;
1227 (*grapheme)[i].cluster=p-text;
1228 last_glyph=(*grapheme)[i].index;
1229 }
1230 return((size_t) i);
1231 #endif
1232 }
1233
IsEmptyOutline(FT_Outline outline)1234 static inline MagickBooleanType IsEmptyOutline(FT_Outline outline)
1235 {
1236 return((outline.n_points == 0) || (outline.n_contours <= 0) ? MagickTrue :
1237 MagickFalse);
1238 }
1239
TraceCubicBezier(FT_Vector * p,FT_Vector * q,FT_Vector * to,DrawInfo * draw_info)1240 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
1241 DrawInfo *draw_info)
1242 {
1243 AffineMatrix
1244 affine;
1245
1246 char
1247 path[MagickPathExtent];
1248
1249 affine=draw_info->affine;
1250 (void) FormatLocaleString(path,MagickPathExtent,"C%g,%g %g,%g %g,%g",
1251 affine.tx+p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,affine.ty-
1252 q->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1253 (void) ConcatenateString(&draw_info->primitive,path);
1254 return(0);
1255 }
1256
TraceLineTo(FT_Vector * to,DrawInfo * draw_info)1257 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
1258 {
1259 AffineMatrix
1260 affine;
1261
1262 char
1263 path[MagickPathExtent];
1264
1265 affine=draw_info->affine;
1266 (void) FormatLocaleString(path,MagickPathExtent,"L%g,%g",affine.tx+to->x/64.0,
1267 affine.ty-to->y/64.0);
1268 (void) ConcatenateString(&draw_info->primitive,path);
1269 return(0);
1270 }
1271
TraceMoveTo(FT_Vector * to,DrawInfo * draw_info)1272 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
1273 {
1274 AffineMatrix
1275 affine;
1276
1277 char
1278 path[MagickPathExtent];
1279
1280 affine=draw_info->affine;
1281 (void) FormatLocaleString(path,MagickPathExtent,"M%g,%g",affine.tx+to->x/64.0,
1282 affine.ty-to->y/64.0);
1283 (void) ConcatenateString(&draw_info->primitive,path);
1284 return(0);
1285 }
1286
TraceQuadraticBezier(FT_Vector * control,FT_Vector * to,DrawInfo * draw_info)1287 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
1288 DrawInfo *draw_info)
1289 {
1290 AffineMatrix
1291 affine;
1292
1293 char
1294 path[MagickPathExtent];
1295
1296 affine=draw_info->affine;
1297 (void) FormatLocaleString(path,MagickPathExtent,"Q%g,%g %g,%g",affine.tx+
1298 control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,affine.ty-
1299 to->y/64.0);
1300 (void) ConcatenateString(&draw_info->primitive,path);
1301 return(0);
1302 }
1303
RenderFreetype(Image * image,const DrawInfo * draw_info,const char * encoding,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)1304 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1305 const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1306 ExceptionInfo *exception)
1307 {
1308 #if !defined(FT_OPEN_PATHNAME)
1309 #define FT_OPEN_PATHNAME ft_open_pathname
1310 #endif
1311
1312 typedef struct _GlyphInfo
1313 {
1314 FT_UInt
1315 id;
1316
1317 FT_Vector
1318 origin;
1319
1320 FT_Glyph
1321 image;
1322 } GlyphInfo;
1323
1324 const char
1325 *value;
1326
1327 DrawInfo
1328 *annotate_info;
1329
1330 FT_BBox
1331 bounds;
1332
1333 FT_BitmapGlyph
1334 bitmap;
1335
1336 FT_Encoding
1337 encoding_type;
1338
1339 FT_Error
1340 ft_status;
1341
1342 FT_Face
1343 face;
1344
1345 FT_Int32
1346 flags;
1347
1348 FT_Library
1349 library;
1350
1351 FT_Matrix
1352 affine;
1353
1354 FT_Open_Args
1355 args;
1356
1357 FT_UInt
1358 first_glyph_id,
1359 last_glyph_id,
1360 missing_glyph_id;
1361
1362 FT_Vector
1363 origin;
1364
1365 GlyphInfo
1366 glyph;
1367
1368 GraphemeInfo
1369 *grapheme;
1370
1371 MagickBooleanType
1372 status;
1373
1374 PointInfo
1375 point,
1376 resolution;
1377
1378 char
1379 *p;
1380
1381 ssize_t
1382 i;
1383
1384 size_t
1385 length;
1386
1387 ssize_t
1388 code,
1389 last_character,
1390 y;
1391
1392 static FT_Outline_Funcs
1393 OutlineMethods =
1394 {
1395 (FT_Outline_MoveTo_Func) TraceMoveTo,
1396 (FT_Outline_LineTo_Func) TraceLineTo,
1397 (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1398 (FT_Outline_CubicTo_Func) TraceCubicBezier,
1399 0, 0
1400 };
1401
1402 unsigned char
1403 *utf8;
1404
1405 /*
1406 Initialize Truetype library.
1407 */
1408 ft_status=FT_Init_FreeType(&library);
1409 if (ft_status != 0)
1410 ThrowBinaryException(TypeError,"UnableToInitializeFreetypeLibrary",
1411 image->filename);
1412 args.flags=FT_OPEN_PATHNAME;
1413 if (draw_info->font == (char *) NULL)
1414 args.pathname=ConstantString("helvetica");
1415 else
1416 if (*draw_info->font != '@')
1417 args.pathname=ConstantString(draw_info->font);
1418 else
1419 args.pathname=ConstantString(draw_info->font+1);
1420 face=(FT_Face) NULL;
1421 ft_status=FT_Open_Face(library,&args,(long) draw_info->face,&face);
1422 if (ft_status != 0)
1423 {
1424 (void) FT_Done_FreeType(library);
1425 (void) ThrowMagickException(exception,GetMagickModule(),TypeError,
1426 "UnableToReadFont","`%s'",args.pathname);
1427 args.pathname=DestroyString(args.pathname);
1428 return(MagickFalse);
1429 }
1430 args.pathname=DestroyString(args.pathname);
1431 if ((draw_info->metrics != (char *) NULL) &&
1432 (IsPathAccessible(draw_info->metrics) != MagickFalse))
1433 (void) FT_Attach_File(face,draw_info->metrics);
1434 encoding_type=FT_ENCODING_UNICODE;
1435 ft_status=FT_Select_Charmap(face,encoding_type);
1436 if ((ft_status != 0) && (face->num_charmaps != 0))
1437 ft_status=FT_Set_Charmap(face,face->charmaps[0]);
1438 if (encoding != (const char *) NULL)
1439 {
1440 if (LocaleCompare(encoding,"AdobeCustom") == 0)
1441 encoding_type=FT_ENCODING_ADOBE_CUSTOM;
1442 if (LocaleCompare(encoding,"AdobeExpert") == 0)
1443 encoding_type=FT_ENCODING_ADOBE_EXPERT;
1444 if (LocaleCompare(encoding,"AdobeStandard") == 0)
1445 encoding_type=FT_ENCODING_ADOBE_STANDARD;
1446 if (LocaleCompare(encoding,"AppleRoman") == 0)
1447 encoding_type=FT_ENCODING_APPLE_ROMAN;
1448 if (LocaleCompare(encoding,"BIG5") == 0)
1449 encoding_type=FT_ENCODING_BIG5;
1450 #if defined(FT_ENCODING_PRC)
1451 if (LocaleCompare(encoding,"GB2312") == 0)
1452 encoding_type=FT_ENCODING_PRC;
1453 #endif
1454 #if defined(FT_ENCODING_JOHAB)
1455 if (LocaleCompare(encoding,"Johab") == 0)
1456 encoding_type=FT_ENCODING_JOHAB;
1457 #endif
1458 #if defined(FT_ENCODING_ADOBE_LATIN_1)
1459 if (LocaleCompare(encoding,"Latin-1") == 0)
1460 encoding_type=FT_ENCODING_ADOBE_LATIN_1;
1461 #endif
1462 #if defined(FT_ENCODING_ADOBE_LATIN_2)
1463 if (LocaleCompare(encoding,"Latin-2") == 0)
1464 encoding_type=FT_ENCODING_OLD_LATIN_2;
1465 #endif
1466 if (LocaleCompare(encoding,"None") == 0)
1467 encoding_type=FT_ENCODING_NONE;
1468 if (LocaleCompare(encoding,"SJIScode") == 0)
1469 encoding_type=FT_ENCODING_SJIS;
1470 if (LocaleCompare(encoding,"Symbol") == 0)
1471 encoding_type=FT_ENCODING_MS_SYMBOL;
1472 if (LocaleCompare(encoding,"Unicode") == 0)
1473 encoding_type=FT_ENCODING_UNICODE;
1474 if (LocaleCompare(encoding,"Wansung") == 0)
1475 encoding_type=FT_ENCODING_WANSUNG;
1476 ft_status=FT_Select_Charmap(face,encoding_type);
1477 if (ft_status != 0)
1478 {
1479 (void) FT_Done_Face(face);
1480 (void) FT_Done_FreeType(library);
1481 ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
1482 }
1483 }
1484 /*
1485 Set text size.
1486 */
1487 resolution.x=DefaultResolution;
1488 resolution.y=DefaultResolution;
1489 if (draw_info->density != (char *) NULL)
1490 {
1491 GeometryInfo
1492 geometry_info;
1493
1494 MagickStatusType
1495 geometry_flags;
1496
1497 geometry_flags=ParseGeometry(draw_info->density,&geometry_info);
1498 resolution.x=geometry_info.rho;
1499 resolution.y=geometry_info.sigma;
1500 if ((geometry_flags & SigmaValue) == 0)
1501 resolution.y=resolution.x;
1502 }
1503 ft_status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1504 (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1505 (FT_UInt) resolution.y);
1506 if (ft_status != 0)
1507 {
1508 (void) FT_Done_Face(face);
1509 (void) FT_Done_FreeType(library);
1510 ThrowBinaryException(TypeError,"UnableToReadFont",draw_info->font);
1511 }
1512 metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1513 metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1514 metrics->ascent=(double) face->size->metrics.ascender/64.0;
1515 metrics->descent=(double) face->size->metrics.descender/64.0;
1516 if (face->size->metrics.ascender == 0)
1517 {
1518 /*
1519 Sanitize buggy ascender and descender values.
1520 */
1521 metrics->ascent=face->size->metrics.y_ppem;
1522 if (face->size->metrics.descender == 0)
1523 metrics->descent=face->size->metrics.y_ppem/-3.5;
1524 }
1525 metrics->width=0;
1526 metrics->origin.x=0;
1527 metrics->origin.y=0;
1528 metrics->height=(double) face->size->metrics.height/64.0;
1529 metrics->max_advance=0.0;
1530 if (face->size->metrics.max_advance > MagickEpsilon)
1531 metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1532 metrics->bounds.x1=0.0;
1533 metrics->bounds.y1=metrics->descent;
1534 metrics->bounds.x2=metrics->ascent+metrics->descent;
1535 metrics->bounds.y2=metrics->ascent+metrics->descent;
1536 metrics->underline_position=face->underline_position*
1537 (metrics->pixels_per_em.x*PerceptibleReciprocal(face->units_per_EM));
1538 metrics->underline_thickness=face->underline_thickness*
1539 (metrics->pixels_per_em.x*PerceptibleReciprocal(face->units_per_EM));
1540 first_glyph_id=0;
1541 FT_Get_First_Char(face,&first_glyph_id);
1542 if ((draw_info->text == (char *) NULL) || (*draw_info->text == '\0') ||
1543 (first_glyph_id == 0))
1544 {
1545 (void) FT_Done_Face(face);
1546 (void) FT_Done_FreeType(library);
1547 return(MagickTrue);
1548 }
1549 /*
1550 Compute bounding box.
1551 */
1552 if (image->debug != MagickFalse)
1553 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
1554 "font-encoding %s; text-encoding %s; pointsize %g",
1555 draw_info->font != (char *) NULL ? draw_info->font : "none",
1556 encoding != (char *) NULL ? encoding : "none",
1557 draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1558 draw_info->pointsize);
1559 flags=FT_LOAD_DEFAULT;
1560 if (draw_info->render == MagickFalse)
1561 flags=FT_LOAD_NO_BITMAP;
1562 if (draw_info->text_antialias == MagickFalse)
1563 flags|=FT_LOAD_RENDER | FT_LOAD_TARGET_MONO;
1564 else
1565 {
1566 #if defined(FT_LOAD_TARGET_LIGHT)
1567 flags|=FT_LOAD_TARGET_LIGHT;
1568 #elif defined(FT_LOAD_TARGET_LCD)
1569 flags|=FT_LOAD_TARGET_LCD;
1570 #endif
1571 }
1572 value=GetImageProperty(image,"type:hinting",exception);
1573 if ((value != (const char *) NULL) && (LocaleCompare(value,"off") == 0))
1574 flags|=FT_LOAD_NO_HINTING;
1575 glyph.id=0;
1576 glyph.image=(FT_Glyph) NULL;
1577 last_glyph_id=0;
1578 origin.x=0;
1579 origin.y=0;
1580 affine.xx=65536L;
1581 affine.yx=0L;
1582 affine.xy=0L;
1583 affine.yy=65536L;
1584 if (draw_info->render != MagickFalse)
1585 {
1586 affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1587 affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1588 affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1589 affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1590 }
1591 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1592 if (annotate_info->dash_pattern != (double *) NULL)
1593 annotate_info->dash_pattern[0]=0.0;
1594 (void) CloneString(&annotate_info->primitive,"path '");
1595 status=MagickTrue;
1596 if (draw_info->render != MagickFalse)
1597 {
1598 if (image->storage_class != DirectClass)
1599 (void) SetImageStorageClass(image,DirectClass,exception);
1600 if (image->alpha_trait == UndefinedPixelTrait)
1601 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1602 }
1603 for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
1604 if (GetUTFCode(p) < 0)
1605 break;
1606 utf8=(unsigned char *) NULL;
1607 if (GetUTFCode(p) == 0)
1608 p=draw_info->text;
1609 else
1610 {
1611 utf8=ConvertLatin1ToUTF8((unsigned char *) draw_info->text);
1612 if (utf8 != (unsigned char *) NULL)
1613 p=(char *) utf8;
1614 }
1615 grapheme=(GraphemeInfo *) NULL;
1616 length=ComplexTextLayout(image,draw_info,p,strlen(p),face,flags,&grapheme,
1617 exception);
1618 missing_glyph_id=FT_Get_Char_Index(face,' ');
1619 code=0;
1620 last_character=(ssize_t) length-1;
1621 for (i=0; i < (ssize_t) length; i++)
1622 {
1623 FT_Outline
1624 outline;
1625
1626 /*
1627 Render UTF-8 sequence.
1628 */
1629 glyph.id=(FT_UInt) grapheme[i].index;
1630 if (glyph.id == 0)
1631 glyph.id=missing_glyph_id;
1632 if ((glyph.id != 0) && (last_glyph_id != 0))
1633 origin.x+=(FT_Pos) (64.0*draw_info->kerning);
1634 glyph.origin=origin;
1635 glyph.origin.x+=(FT_Pos) grapheme[i].x_offset;
1636 glyph.origin.y+=(FT_Pos) grapheme[i].y_offset;
1637 if (glyph.image != (FT_Glyph) NULL)
1638 {
1639 FT_Done_Glyph(glyph.image);
1640 glyph.image=(FT_Glyph) NULL;
1641 }
1642 ft_status=FT_Load_Glyph(face,glyph.id,flags);
1643 if (ft_status != 0)
1644 continue;
1645 ft_status=FT_Get_Glyph(face->glyph,&glyph.image);
1646 if (ft_status != 0)
1647 continue;
1648 outline=((FT_OutlineGlyph) glyph.image)->outline;
1649 if ((glyph.image->format != FT_GLYPH_FORMAT_OUTLINE) &&
1650 (IsEmptyOutline(outline) == MagickFalse))
1651 continue;
1652 ft_status=FT_Outline_Get_BBox(&outline,&bounds);
1653 if (ft_status != 0)
1654 continue;
1655 if ((p == draw_info->text) || (bounds.xMin < metrics->bounds.x1))
1656 if (bounds.xMin != 0)
1657 metrics->bounds.x1=(double) bounds.xMin;
1658 if ((p == draw_info->text) || (bounds.yMin < metrics->bounds.y1))
1659 if (bounds.yMin != 0)
1660 metrics->bounds.y1=(double) bounds.yMin;
1661 if ((p == draw_info->text) || (bounds.xMax > metrics->bounds.x2))
1662 if (bounds.xMax != 0)
1663 metrics->bounds.x2=(double) bounds.xMax;
1664 if ((p == draw_info->text) || (bounds.yMax > metrics->bounds.y2))
1665 if (bounds.yMax != 0)
1666 metrics->bounds.y2=(double) bounds.yMax;
1667 if (((draw_info->stroke.alpha != TransparentAlpha) ||
1668 (draw_info->stroke_pattern != (Image *) NULL)) &&
1669 ((status != MagickFalse) && (draw_info->render != MagickFalse)))
1670 {
1671 /*
1672 Trace the glyph.
1673 */
1674 annotate_info->affine.tx=glyph.origin.x/64.0;
1675 annotate_info->affine.ty=(-glyph.origin.y/64.0);
1676 if (IsEmptyOutline(outline) == MagickFalse)
1677 ft_status=FT_Outline_Decompose(&outline,&OutlineMethods,
1678 annotate_info);
1679 }
1680 FT_Vector_Transform(&glyph.origin,&affine);
1681 (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1682 ft_status=FT_Glyph_To_Bitmap(&glyph.image,FT_RENDER_MODE_NORMAL,
1683 (FT_Vector *) NULL,MagickTrue);
1684 if (ft_status != 0)
1685 continue;
1686 bitmap=(FT_BitmapGlyph) glyph.image;
1687 point.x=offset->x+bitmap->left;
1688 if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1689 point.x+=(origin.x/64.0);
1690 point.y=offset->y-bitmap->top;
1691 if (draw_info->render != MagickFalse)
1692 {
1693 CacheView
1694 *image_view;
1695
1696 MagickBooleanType
1697 transparent_fill;
1698
1699 unsigned char
1700 *r;
1701
1702 /*
1703 Rasterize the glyph.
1704 */
1705 transparent_fill=((draw_info->fill.alpha == TransparentAlpha) &&
1706 (draw_info->fill_pattern == (Image *) NULL) &&
1707 (draw_info->stroke.alpha == TransparentAlpha) &&
1708 (draw_info->stroke_pattern == (Image *) NULL)) ? MagickTrue :
1709 MagickFalse;
1710 image_view=AcquireAuthenticCacheView(image,exception);
1711 r=bitmap->bitmap.buffer;
1712 for (y=0; y < (ssize_t) bitmap->bitmap.rows; y++)
1713 {
1714 double
1715 fill_opacity;
1716
1717 MagickBooleanType
1718 active,
1719 sync;
1720
1721 PixelInfo
1722 fill_color;
1723
1724 Quantum
1725 *magick_restrict q;
1726
1727 ssize_t
1728 x;
1729
1730 ssize_t
1731 n,
1732 x_offset,
1733 y_offset;
1734
1735 if (status == MagickFalse)
1736 continue;
1737 x_offset=CastDoubleToLong(ceil(point.x-0.5));
1738 y_offset=CastDoubleToLong(ceil(point.y+y-0.5));
1739 if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
1740 continue;
1741 q=(Quantum *) NULL;
1742 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1743 active=MagickFalse;
1744 else
1745 {
1746 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
1747 bitmap->bitmap.width,1,exception);
1748 active=q != (Quantum *) NULL ? MagickTrue : MagickFalse;
1749 }
1750 n=y*bitmap->bitmap.pitch;
1751 for (x=0; x < (ssize_t) bitmap->bitmap.width; x++, n++)
1752 {
1753 x_offset=CastDoubleToLong(ceil(point.x+x-0.5));
1754 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1755 {
1756 if (q != (Quantum *) NULL)
1757 q+=GetPixelChannels(image);
1758 continue;
1759 }
1760 fill_opacity=1.0;
1761 if (bitmap->bitmap.buffer != (unsigned char *) NULL)
1762 {
1763 if (bitmap->bitmap.pixel_mode == ft_pixel_mode_grays)
1764 fill_opacity=(double) (r[n])/(bitmap->bitmap.num_grays-1);
1765 else
1766 if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1767 fill_opacity=((r[(x >> 3)+y*bitmap->bitmap.pitch] &
1768 (1 << (~x & 0x07)))) == 0 ? 0.0 : 1.0;
1769 }
1770 if (draw_info->text_antialias == MagickFalse)
1771 fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
1772 if (active == MagickFalse)
1773 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
1774 exception);
1775 if (q == (Quantum *) NULL)
1776 continue;
1777 if (transparent_fill == MagickFalse)
1778 {
1779 GetPixelInfo(image,&fill_color);
1780 GetFillColor(draw_info,x_offset,y_offset,&fill_color,exception);
1781 fill_opacity=fill_opacity*fill_color.alpha;
1782 CompositePixelOver(image,&fill_color,fill_opacity,q,
1783 GetPixelAlpha(image,q),q);
1784 }
1785 else
1786 {
1787 double
1788 Sa,
1789 Da;
1790
1791 Da=1.0-(QuantumScale*GetPixelAlpha(image,q));
1792 Sa=fill_opacity;
1793 fill_opacity=(1.0-RoundToUnity(Sa+Da-Sa*Da))*QuantumRange;
1794 SetPixelAlpha(image,fill_opacity,q);
1795 }
1796 if (active == MagickFalse)
1797 {
1798 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1799 if (sync == MagickFalse)
1800 status=MagickFalse;
1801 }
1802 q+=GetPixelChannels(image);
1803 }
1804 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1805 if (sync == MagickFalse)
1806 status=MagickFalse;
1807 }
1808 image_view=DestroyCacheView(image_view);
1809 if (((draw_info->stroke.alpha != TransparentAlpha) ||
1810 (draw_info->stroke_pattern != (Image *) NULL)) &&
1811 (status != MagickFalse))
1812 {
1813 /*
1814 Draw text stroke.
1815 */
1816 annotate_info->linejoin=RoundJoin;
1817 annotate_info->affine.tx=offset->x;
1818 annotate_info->affine.ty=offset->y;
1819 (void) ConcatenateString(&annotate_info->primitive,"'");
1820 if (strlen(annotate_info->primitive) > 7)
1821 (void) DrawImage(image,annotate_info,exception);
1822 (void) CloneString(&annotate_info->primitive,"path '");
1823 }
1824 }
1825 if ((fabs(draw_info->interword_spacing) >= MagickEpsilon) &&
1826 (IsUTFSpace(GetUTFCode(p+grapheme[i].cluster)) != MagickFalse) &&
1827 (IsUTFSpace(code) == MagickFalse))
1828 origin.x+=(FT_Pos) (64.0*draw_info->interword_spacing);
1829 else
1830 if (i == last_character)
1831 origin.x+=MagickMax((FT_Pos) grapheme[i].x_advance,bounds.xMax);
1832 else
1833 origin.x+=(FT_Pos) grapheme[i].x_advance;
1834 metrics->origin.x=(double) origin.x;
1835 metrics->origin.y=(double) origin.y;
1836 if (metrics->origin.x > metrics->width)
1837 metrics->width=metrics->origin.x;
1838 last_glyph_id=glyph.id;
1839 code=GetUTFCode(p+grapheme[i].cluster);
1840 }
1841 if (grapheme != (GraphemeInfo *) NULL)
1842 grapheme=(GraphemeInfo *) RelinquishMagickMemory(grapheme);
1843 if (utf8 != (unsigned char *) NULL)
1844 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
1845 if (glyph.image != (FT_Glyph) NULL)
1846 {
1847 FT_Done_Glyph(glyph.image);
1848 glyph.image=(FT_Glyph) NULL;
1849 }
1850 /*
1851 Determine font metrics.
1852 */
1853 metrics->bounds.x1/=64.0;
1854 metrics->bounds.y1/=64.0;
1855 metrics->bounds.x2/=64.0;
1856 metrics->bounds.y2/=64.0;
1857 metrics->origin.x/=64.0;
1858 metrics->origin.y/=64.0;
1859 metrics->width=ceil(metrics->width/64.0);
1860 /*
1861 Relinquish resources.
1862 */
1863 annotate_info=DestroyDrawInfo(annotate_info);
1864 (void) FT_Done_Face(face);
1865 (void) FT_Done_FreeType(library);
1866 return(status);
1867 }
1868 #else
RenderFreetype(Image * image,const DrawInfo * draw_info,const char * magick_unused (encoding),const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)1869 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1870 const char *magick_unused(encoding),const PointInfo *offset,
1871 TypeMetric *metrics,ExceptionInfo *exception)
1872 {
1873 (void) ThrowMagickException(exception,GetMagickModule(),
1874 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (Freetype)",
1875 draw_info->font != (char *) NULL ? draw_info->font : "none");
1876 return(RenderPostscript(image,draw_info,offset,metrics,exception));
1877 }
1878 #endif
1879
1880 /*
1881 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1882 % %
1883 % %
1884 % %
1885 + R e n d e r P o s t s c r i p t %
1886 % %
1887 % %
1888 % %
1889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1890 %
1891 % RenderPostscript() renders text on the image with a Postscript font. It
1892 % also returns the bounding box of the text relative to the image.
1893 %
1894 % The format of the RenderPostscript method is:
1895 %
1896 % MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
1897 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
1898 %
1899 % A description of each parameter follows:
1900 %
1901 % o image: the image.
1902 %
1903 % o draw_info: the draw info.
1904 %
1905 % o offset: (x,y) location of text relative to image.
1906 %
1907 % o metrics: bounding box of text.
1908 %
1909 % o exception: return any errors or warnings in this structure.
1910 %
1911 */
1912
EscapeParenthesis(const char * source)1913 static char *EscapeParenthesis(const char *source)
1914 {
1915 char
1916 *destination;
1917
1918 char
1919 *q;
1920
1921 const char
1922 *p;
1923
1924 size_t
1925 length;
1926
1927 assert(source != (const char *) NULL);
1928 length=0;
1929 for (p=source; *p != '\0'; p++)
1930 {
1931 if ((*p == '\\') || (*p == '(') || (*p == ')'))
1932 {
1933 if (~length < 1)
1934 ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
1935 length++;
1936 }
1937 length++;
1938 }
1939 destination=(char *) NULL;
1940 if (~length >= (MagickPathExtent-1))
1941 destination=(char *) AcquireQuantumMemory(length+MagickPathExtent,
1942 sizeof(*destination));
1943 if (destination == (char *) NULL)
1944 ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
1945 *destination='\0';
1946 q=destination;
1947 for (p=source; *p != '\0'; p++)
1948 {
1949 if ((*p == '\\') || (*p == '(') || (*p == ')'))
1950 *q++='\\';
1951 *q++=(*p);
1952 }
1953 *q='\0';
1954 return(destination);
1955 }
1956
RenderPostscript(Image * image,const DrawInfo * draw_info,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)1957 static MagickBooleanType RenderPostscript(Image *image,
1958 const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics,
1959 ExceptionInfo *exception)
1960 {
1961 char
1962 filename[MagickPathExtent],
1963 geometry[MagickPathExtent],
1964 *text;
1965
1966 FILE
1967 *file;
1968
1969 Image
1970 *annotate_image;
1971
1972 ImageInfo
1973 *annotate_info;
1974
1975 int
1976 unique_file;
1977
1978 MagickBooleanType
1979 identity;
1980
1981 PointInfo
1982 extent,
1983 point,
1984 resolution;
1985
1986 ssize_t
1987 i;
1988
1989 size_t
1990 length;
1991
1992 ssize_t
1993 y;
1994
1995 /*
1996 Render label with a Postscript font.
1997 */
1998 if (image->debug != MagickFalse)
1999 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
2000 "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
2001 draw_info->font : "none",draw_info->pointsize);
2002 file=(FILE *) NULL;
2003 unique_file=AcquireUniqueFileResource(filename);
2004 if (unique_file != -1)
2005 file=fdopen(unique_file,"wb");
2006 if ((unique_file == -1) || (file == (FILE *) NULL))
2007 {
2008 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",filename);
2009 return(MagickFalse);
2010 }
2011 (void) FormatLocaleFile(file,"%%!PS-Adobe-3.0\n");
2012 (void) FormatLocaleFile(file,"/ReencodeType\n");
2013 (void) FormatLocaleFile(file,"{\n");
2014 (void) FormatLocaleFile(file," findfont dup length\n");
2015 (void) FormatLocaleFile(file,
2016 " dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
2017 (void) FormatLocaleFile(file,
2018 " /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
2019 (void) FormatLocaleFile(file,"} bind def\n");
2020 /*
2021 Sample to compute bounding box.
2022 */
2023 identity=(fabs(draw_info->affine.sx-draw_info->affine.sy) < MagickEpsilon) &&
2024 (fabs(draw_info->affine.rx) < MagickEpsilon) &&
2025 (fabs(draw_info->affine.ry) < MagickEpsilon) ? MagickTrue : MagickFalse;
2026 extent.x=0.0;
2027 extent.y=0.0;
2028 length=strlen(draw_info->text);
2029 for (i=0; i <= (ssize_t) (length+2); i++)
2030 {
2031 point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
2032 draw_info->affine.ry*2.0*draw_info->pointsize);
2033 point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
2034 draw_info->affine.sy*2.0*draw_info->pointsize);
2035 if (point.x > extent.x)
2036 extent.x=point.x;
2037 if (point.y > extent.y)
2038 extent.y=point.y;
2039 }
2040 (void) FormatLocaleFile(file,"%g %g moveto\n",identity != MagickFalse ? 0.0 :
2041 extent.x/2.0,extent.y/2.0);
2042 (void) FormatLocaleFile(file,"%g %g scale\n",draw_info->pointsize,
2043 draw_info->pointsize);
2044 if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
2045 (strchr(draw_info->font,'/') != (char *) NULL))
2046 (void) FormatLocaleFile(file,
2047 "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
2048 else
2049 (void) FormatLocaleFile(file,
2050 "/%s-ISO dup /%s ReencodeType findfont setfont\n",draw_info->font,
2051 draw_info->font);
2052 (void) FormatLocaleFile(file,"[%g %g %g %g 0 0] concat\n",
2053 draw_info->affine.sx,-draw_info->affine.rx,-draw_info->affine.ry,
2054 draw_info->affine.sy);
2055 text=EscapeParenthesis(draw_info->text);
2056 if (identity == MagickFalse)
2057 (void) FormatLocaleFile(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",
2058 text);
2059 (void) FormatLocaleFile(file,"(%s) show\n",text);
2060 text=DestroyString(text);
2061 (void) FormatLocaleFile(file,"showpage\n");
2062 (void) fclose(file);
2063 (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0!",
2064 floor(extent.x+0.5),floor(extent.y+0.5));
2065 annotate_info=AcquireImageInfo();
2066 (void) FormatLocaleString(annotate_info->filename,MagickPathExtent,"ps:%s",
2067 filename);
2068 (void) CloneString(&annotate_info->page,geometry);
2069 if (draw_info->density != (char *) NULL)
2070 (void) CloneString(&annotate_info->density,draw_info->density);
2071 annotate_info->antialias=draw_info->text_antialias;
2072 annotate_image=ReadImage(annotate_info,exception);
2073 CatchException(exception);
2074 annotate_info=DestroyImageInfo(annotate_info);
2075 (void) RelinquishUniqueFileResource(filename);
2076 if (annotate_image == (Image *) NULL)
2077 return(MagickFalse);
2078 (void) NegateImage(annotate_image,MagickFalse,exception);
2079 resolution.x=DefaultResolution;
2080 resolution.y=DefaultResolution;
2081 if (draw_info->density != (char *) NULL)
2082 {
2083 GeometryInfo
2084 geometry_info;
2085
2086 MagickStatusType
2087 flags;
2088
2089 flags=ParseGeometry(draw_info->density,&geometry_info);
2090 resolution.x=geometry_info.rho;
2091 resolution.y=geometry_info.sigma;
2092 if ((flags & SigmaValue) == 0)
2093 resolution.y=resolution.x;
2094 }
2095 if (identity == MagickFalse)
2096 (void) TransformImage(&annotate_image,"0x0",(char *) NULL,exception);
2097 else
2098 {
2099 RectangleInfo
2100 crop_info;
2101
2102 crop_info=GetImageBoundingBox(annotate_image,exception);
2103 crop_info.height=(size_t) ((resolution.y/DefaultResolution)*
2104 ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
2105 crop_info.y=CastDoubleToLong(ceil((resolution.y/DefaultResolution)*
2106 extent.y/8.0-0.5));
2107 (void) FormatLocaleString(geometry,MagickPathExtent,
2108 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
2109 crop_info.height,(double) crop_info.x,(double) crop_info.y);
2110 (void) TransformImage(&annotate_image,geometry,(char *) NULL,exception);
2111 }
2112 metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
2113 ExpandAffine(&draw_info->affine)*draw_info->pointsize;
2114 metrics->pixels_per_em.y=metrics->pixels_per_em.x;
2115 metrics->ascent=metrics->pixels_per_em.y;
2116 metrics->descent=metrics->pixels_per_em.y/-5.0;
2117 metrics->width=(double) annotate_image->columns/
2118 ExpandAffine(&draw_info->affine);
2119 metrics->height=floor(metrics->ascent-metrics->descent+0.5);
2120 metrics->max_advance=metrics->pixels_per_em.x;
2121 metrics->bounds.x1=0.0;
2122 metrics->bounds.y1=metrics->descent;
2123 metrics->bounds.x2=metrics->ascent+metrics->descent;
2124 metrics->bounds.y2=metrics->ascent+metrics->descent;
2125 metrics->underline_position=(-2.0);
2126 metrics->underline_thickness=1.0;
2127 if (draw_info->render == MagickFalse)
2128 {
2129 annotate_image=DestroyImage(annotate_image);
2130 return(MagickTrue);
2131 }
2132 if (draw_info->fill.alpha != TransparentAlpha)
2133 {
2134 CacheView
2135 *annotate_view;
2136
2137 MagickBooleanType
2138 sync;
2139
2140 PixelInfo
2141 fill_color;
2142
2143 /*
2144 Render fill color.
2145 */
2146 if (image->alpha_trait == UndefinedPixelTrait)
2147 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2148 if (annotate_image->alpha_trait == UndefinedPixelTrait)
2149 (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel,
2150 exception);
2151 fill_color=draw_info->fill;
2152 annotate_view=AcquireAuthenticCacheView(annotate_image,exception);
2153 for (y=0; y < (ssize_t) annotate_image->rows; y++)
2154 {
2155 ssize_t
2156 x;
2157
2158 Quantum
2159 *magick_restrict q;
2160
2161 q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
2162 1,exception);
2163 if (q == (Quantum *) NULL)
2164 break;
2165 for (x=0; x < (ssize_t) annotate_image->columns; x++)
2166 {
2167 GetFillColor(draw_info,x,y,&fill_color,exception);
2168 SetPixelAlpha(annotate_image,ClampToQuantum((((double) QuantumScale*
2169 GetPixelIntensity(annotate_image,q)*fill_color.alpha))),q);
2170 SetPixelRed(annotate_image,fill_color.red,q);
2171 SetPixelGreen(annotate_image,fill_color.green,q);
2172 SetPixelBlue(annotate_image,fill_color.blue,q);
2173 q+=GetPixelChannels(annotate_image);
2174 }
2175 sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
2176 if (sync == MagickFalse)
2177 break;
2178 }
2179 annotate_view=DestroyCacheView(annotate_view);
2180 (void) CompositeImage(image,annotate_image,OverCompositeOp,MagickTrue,
2181 (ssize_t) ceil(offset->x-0.5),(ssize_t) ceil(offset->y-(metrics->ascent+
2182 metrics->descent)-0.5),exception);
2183 }
2184 annotate_image=DestroyImage(annotate_image);
2185 return(MagickTrue);
2186 }
2187
2188 /*
2189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2190 % %
2191 % %
2192 % %
2193 + R e n d e r X 1 1 %
2194 % %
2195 % %
2196 % %
2197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2198 %
2199 % RenderX11() renders text on the image with an X11 font. It also returns the
2200 % bounding box of the text relative to the image.
2201 %
2202 % The format of the RenderX11 method is:
2203 %
2204 % MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
2205 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2206 %
2207 % A description of each parameter follows:
2208 %
2209 % o image: the image.
2210 %
2211 % o draw_info: the draw info.
2212 %
2213 % o offset: (x,y) location of text relative to image.
2214 %
2215 % o metrics: bounding box of text.
2216 %
2217 % o exception: return any errors or warnings in this structure.
2218 %
2219 */
RenderX11(Image * image,const DrawInfo * draw_info,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)2220 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
2221 const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2222 {
2223 MagickBooleanType
2224 status;
2225
2226 if (annotate_semaphore == (SemaphoreInfo *) NULL)
2227 ActivateSemaphoreInfo(&annotate_semaphore);
2228 LockSemaphoreInfo(annotate_semaphore);
2229 status=XRenderImage(image,draw_info,offset,metrics,exception);
2230 UnlockSemaphoreInfo(annotate_semaphore);
2231 return(status);
2232 }
2233