]> granicus.if.org Git - imagemagick/blob - magick/annotate.c
(no commit message)
[imagemagick] / magick / annotate.c
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 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Digital Applications (www.digapp.com) contributed the stroked text algorithm.
37 % It was written by Leonard Rosenthol.
38 %
39 %
40 */
41 \f
42 /*
43   Include declarations.
44 */
45 #include "magick/studio.h"
46 #include "magick/annotate.h"
47 #include "magick/attribute.h"
48 #include "magick/cache-view.h"
49 #include "magick/client.h"
50 #include "magick/color.h"
51 #include "magick/color-private.h"
52 #include "magick/composite.h"
53 #include "magick/composite-private.h"
54 #include "magick/constitute.h"
55 #include "magick/draw.h"
56 #include "magick/draw-private.h"
57 #include "magick/exception.h"
58 #include "magick/exception-private.h"
59 #include "magick/gem.h"
60 #include "magick/geometry.h"
61 #include "magick/image-private.h"
62 #include "magick/log.h"
63 #include "magick/quantum.h"
64 #include "magick/quantum-private.h"
65 #include "magick/property.h"
66 #include "magick/resource_.h"
67 #include "magick/statistic.h"
68 #include "magick/string_.h"
69 #include "magick/token-private.h"
70 #include "magick/transform.h"
71 #include "magick/type.h"
72 #include "magick/utility.h"
73 #include "magick/xwindow-private.h"
74 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
75 #if defined(__MINGW32__)
76 #  undef interface
77 #endif
78 #if defined(MAGICKCORE_HAVE_FT2BUILD_H)
79 #  include <ft2build.h>
80 #endif
81 #if defined(FT_FREETYPE_H)
82 #  include FT_FREETYPE_H
83 #else
84 #  include <freetype/freetype.h>
85 #endif
86 #if defined(FT_GLYPH_H)
87 #  include FT_GLYPH_H
88 #else
89 #  include <freetype/ftglyph.h>
90 #endif
91 #if defined(FT_OUTLINE_H)
92 #  include FT_OUTLINE_H
93 #else
94 #  include <freetype/ftoutln.h>
95 #endif
96 #if defined(FT_BBOX_H)
97 #  include FT_BBOX_H
98 #else
99 #  include <freetype/ftbbox.h>
100 #endif /* defined(FT_BBOX_H) */
101 #endif
102 \f
103 /*
104   Forward declarations.
105 */
106 static MagickBooleanType
107   RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
108   RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
109   RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
110     TypeMetric *),
111   RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *);
112 \f
113 /*
114 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115 %                                                                             %
116 %                                                                             %
117 %                                                                             %
118 %   A n n o t a t e I m a g e                                                 %
119 %                                                                             %
120 %                                                                             %
121 %                                                                             %
122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123 %
124 %  AnnotateImage() annotates an image with text.  Optionally you can include
125 %  any of the following bits of information about the image by embedding
126 %  the appropriate special characters:
127 %
128 %    %b   file size in bytes.
129 %    %c   comment.
130 %    %d   directory in which the image resides.
131 %    %e   extension of the image file.
132 %    %f   original filename of the image.
133 %    %h   height of image.
134 %    %i   filename of the image.
135 %    %k   number of unique colors.
136 %    %l   image label.
137 %    %m   image file format.
138 %    %n   number of images in a image sequence.
139 %    %o   output image filename.
140 %    %p   page number of the image.
141 %    %q   image depth (8 or 16).
142 %    %q   image depth (8 or 16).
143 %    %s   image scene number.
144 %    %t   image filename without any extension.
145 %    %u   a unique temporary filename.
146 %    %w   image width.
147 %    %x   x resolution of the image.
148 %    %y   y resolution of the image.
149 %
150 %  The format of the AnnotateImage method is:
151 %
152 %      MagickBooleanType AnnotateImage(Image *image,DrawInfo *draw_info)
153 %
154 %  A description of each parameter follows:
155 %
156 %    o image: the image.
157 %
158 %    o draw_info: the draw info.
159 %
160 */
161 MagickExport MagickBooleanType AnnotateImage(Image *image,
162   const DrawInfo *draw_info)
163 {
164   char
165     primitive[MaxTextExtent],
166     **textlist;
167
168   DrawInfo
169     *annotate,
170     *annotate_info;
171
172   GeometryInfo
173     geometry_info;
174
175   MagickBooleanType
176     status;
177
178   PointInfo
179     offset;
180
181   RectangleInfo
182     geometry;
183
184   register ssize_t
185     i;
186
187   size_t
188     length;
189
190   TypeMetric
191     metrics;
192
193   size_t
194     height,
195     number_lines;
196
197   assert(image != (Image *) NULL);
198   assert(image->signature == MagickSignature);
199   if (image->debug != MagickFalse)
200     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
201   assert(draw_info != (DrawInfo *) NULL);
202   assert(draw_info->signature == MagickSignature);
203   if (draw_info->text == (char *) NULL)
204     return(MagickFalse);
205   if (*draw_info->text == '\0')
206     return(MagickTrue);
207   textlist=StringToList(draw_info->text);
208   if (textlist == (char **) NULL)
209     return(MagickFalse);
210   length=strlen(textlist[0]);
211   for (i=1; textlist[i] != (char *) NULL; i++)
212     if (strlen(textlist[i]) > length)
213       length=strlen(textlist[i]);
214   number_lines=(size_t) i;
215   annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
216   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
217   SetGeometry(image,&geometry);
218   SetGeometryInfo(&geometry_info);
219   if (annotate_info->geometry != (char *) NULL)
220     {
221       (void) ParsePageGeometry(image,annotate_info->geometry,&geometry,
222         &image->exception);
223       (void) ParseGeometry(annotate_info->geometry,&geometry_info);
224     }
225   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
226     return(MagickFalse);
227   status=MagickTrue;
228   for (i=0; textlist[i] != (char *) NULL; i++)
229   {
230     /*
231       Position text relative to image.
232     */
233     annotate_info->affine.tx=geometry_info.xi-image->page.x;
234     annotate_info->affine.ty=geometry_info.psi-image->page.y;
235     (void) CloneString(&annotate->text,textlist[i]);
236     (void) GetTypeMetrics(image,annotate,&metrics);
237     height=(ssize_t) (metrics.ascent-metrics.descent+
238       draw_info->interline_spacing+0.5);
239     switch (annotate->gravity)
240     {
241       case UndefinedGravity:
242       default:
243       {
244         offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
245         offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
246         break;
247       }
248       case NorthWestGravity:
249       {
250         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
251           annotate_info->affine.ry*height+annotate_info->affine.ry*
252           (metrics.ascent+metrics.descent);
253         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
254           annotate_info->affine.sy*height+annotate_info->affine.sy*
255           metrics.ascent;
256         break;
257       }
258       case NorthGravity:
259       {
260         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
261           geometry.width/2.0+i*annotate_info->affine.ry*height-
262           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
263           annotate_info->affine.ry*(metrics.ascent+metrics.descent);
264         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
265           annotate_info->affine.sy*height+annotate_info->affine.sy*
266           metrics.ascent-annotate_info->affine.rx*(metrics.width-
267           metrics.bounds.x1)/2.0;
268         break;
269       }
270       case NorthEastGravity:
271       {
272         offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
273           geometry.width+i*annotate_info->affine.ry*height-
274           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
275           annotate_info->affine.ry*(metrics.ascent+metrics.descent)-1.0;
276         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
277           annotate_info->affine.sy*height+annotate_info->affine.sy*
278           metrics.ascent-annotate_info->affine.rx*(metrics.width-
279           metrics.bounds.x1);
280         break;
281       }
282       case WestGravity:
283       {
284         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
285           annotate_info->affine.ry*height+annotate_info->affine.ry*
286           (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
287         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
288           geometry.height/2.0+i*annotate_info->affine.sy*height+
289           annotate_info->affine.sy*(metrics.ascent+metrics.descent-
290           (number_lines-1.0)*height)/2.0;
291         break;
292       }
293       case StaticGravity:
294       case CenterGravity:
295       {
296         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
297           geometry.width/2.0+i*annotate_info->affine.ry*height-
298           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
299           annotate_info->affine.ry*(metrics.ascent+metrics.descent-
300           (number_lines-1)*height)/2.0;
301         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
302           geometry.height/2.0+i*annotate_info->affine.sy*height-
303           annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0+
304           annotate_info->affine.sy*(metrics.ascent+metrics.descent-
305           (number_lines-1.0)*height)/2.0;
306         break;
307       }
308       case EastGravity:
309       {
310         offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
311           geometry.width+i*annotate_info->affine.ry*height-
312           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
313           annotate_info->affine.ry*(metrics.ascent+metrics.descent-
314           (number_lines-1.0)*height)/2.0-1.0;
315         offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
316           geometry.height/2.0+i*annotate_info->affine.sy*height-
317           annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)+
318           annotate_info->affine.sy*(metrics.ascent+metrics.descent-
319           (number_lines-1.0)*height)/2.0;
320         break;
321       }
322       case SouthWestGravity:
323       {
324         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
325           annotate_info->affine.ry*height-annotate_info->affine.ry*
326           (number_lines-1.0)*height;
327         offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
328           geometry.height+i*annotate_info->affine.sy*height-
329           annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
330         break;
331       }
332       case SouthGravity:
333       {
334         offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
335           geometry.width/2.0+i*annotate_info->affine.ry*height-
336           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0-
337           annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
338         offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
339           geometry.height+i*annotate_info->affine.sy*height-
340           annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0-
341           annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
342         break;
343       }
344       case SouthEastGravity:
345       {
346         offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
347           geometry.width+i*annotate_info->affine.ry*height-
348           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)-
349           annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
350         offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
351           geometry.height+i*annotate_info->affine.sy*height-
352           annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)-
353           annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
354         break;
355       }
356     }
357     switch (annotate->align)
358     {
359       case LeftAlign:
360       {
361         offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
362         offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
363         break;
364       }
365       case CenterAlign:
366       {
367         offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
368           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0;
369         offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
370           annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0;
371         break;
372       }
373       case RightAlign:
374       {
375         offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
376           annotate_info->affine.sx*(metrics.width+metrics.bounds.x1);
377         offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
378           annotate_info->affine.rx*(metrics.width+metrics.bounds.x1);
379         break;
380       }
381       default:
382         break;
383     }
384     if (draw_info->undercolor.opacity != TransparentOpacity)
385       {
386         DrawInfo
387           *undercolor_info;
388
389         /*
390           Text box.
391         */
392         undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
393         undercolor_info->fill=draw_info->undercolor;
394         undercolor_info->affine=draw_info->affine;
395         undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
396         undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
397         (void) FormatMagickString(primitive,MaxTextExtent,
398           "rectangle 0,0 %g,%.20g",metrics.origin.x,(double) height);
399         (void) CloneString(&undercolor_info->primitive,primitive);
400         (void) DrawImage(image,undercolor_info);
401         (void) DestroyDrawInfo(undercolor_info);
402       }
403     annotate_info->affine.tx=offset.x;
404     annotate_info->affine.ty=offset.y;
405     (void) FormatMagickString(primitive,MaxTextExtent,"stroke-width %g "
406       "line 0,0 %g,0",metrics.underline_thickness,metrics.width);
407     if (annotate->decorate == OverlineDecoration)
408       {
409         annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
410           metrics.descent-metrics.underline_position));
411         (void) CloneString(&annotate_info->primitive,primitive);
412         (void) DrawImage(image,annotate_info);
413       }
414     else
415       if (annotate->decorate == UnderlineDecoration)
416         {
417           annotate_info->affine.ty-=(draw_info->affine.sy*
418             metrics.underline_position);
419           (void) CloneString(&annotate_info->primitive,primitive);
420           (void) DrawImage(image,annotate_info);
421         }
422     /*
423       Annotate image with text.
424     */
425     status=RenderType(image,annotate,&offset,&metrics);
426     if (status == MagickFalse)
427       break;
428     if (annotate->decorate == LineThroughDecoration)
429       {
430         annotate_info->affine.ty-=(draw_info->affine.sy*(height+
431           metrics.underline_position+metrics.descent)/2.0);
432         (void) CloneString(&annotate_info->primitive,primitive);
433         (void) DrawImage(image,annotate_info);
434       }
435   }
436   /*
437     Relinquish resources.
438   */
439   annotate_info=DestroyDrawInfo(annotate_info);
440   annotate=DestroyDrawInfo(annotate);
441   for (i=0; textlist[i] != (char *) NULL; i++)
442     textlist[i]=DestroyString(textlist[i]);
443   textlist=(char **) RelinquishMagickMemory(textlist);
444   return(status);
445 }
446 \f
447 /*
448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449 %                                                                             %
450 %                                                                             %
451 %                                                                             %
452 %  F o r m a t M a g i c k C a p t i o n                                      %
453 %                                                                             %
454 %                                                                             %
455 %                                                                             %
456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
457 %
458 %  FormatMagickCaption() formats a caption so that it fits within the image
459 %  width.  It returns the number of lines in the formatted caption.
460 %
461 %  The format of the FormatMagickCaption method is:
462 %
463 %      ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
464 %        const MagickBooleanType split,TypeMetric *metrics,char **caption)
465 %
466 %  A description of each parameter follows.
467 %
468 %    o image:  The image.
469 %
470 %    o draw_info: the draw info.
471 %
472 %    o split: when no convenient line breaks-- insert newline.
473 %
474 %    o metrics: Return the font metrics in this structure.
475 %
476 %    o caption: the caption.
477 %
478 */
479 MagickExport ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
480   const MagickBooleanType split,TypeMetric *metrics,char **caption)
481 {
482   MagickBooleanType
483     status;
484
485   register char
486     *p,
487     *q,
488     *s;
489
490   register ssize_t
491     i;
492
493   size_t
494     width;
495
496   q=draw_info->text;
497   s=(char *) NULL;
498   for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
499   {
500     if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
501       s=p;
502     for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
503       *q++=(*(p+i));
504     *q='\0';
505     status=GetTypeMetrics(image,draw_info,metrics);
506     if (status == MagickFalse)
507       break;
508     width=(size_t) floor(metrics->width+0.5);
509     if (GetUTFCode(p) != '\n')
510       if (width <= image->columns)
511         continue;
512     if (s == (char *) NULL)
513       {
514         s=p;
515         while ((IsUTFSpace(GetUTFCode(s)) == MagickFalse) &&
516                (GetUTFCode(s) != 0))
517           s+=GetUTFOctets(s);
518       }
519     if (GetUTFCode(s) != 0)
520       {
521         *s='\n';
522         p=s;
523       }
524     else
525       if (split != MagickFalse)
526         {
527           char
528             *target;
529
530           ssize_t
531             n;
532
533           /*
534             No convenient line breaks-- insert newline.
535           */
536           target=AcquireString(*caption);
537           n=p-(*caption);
538           CopyMagickString(target,*caption,n+1);
539           ConcatenateMagickString(target,"\n",strlen(*caption)+1);
540           ConcatenateMagickString(target,p,strlen(*caption)+2);
541           (void) DestroyString(*caption);
542           *caption=target;
543           p=(*caption)+n;
544         }
545     s=(char *) NULL;
546     q=draw_info->text;
547   }
548   i=0;
549   for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
550     if (GetUTFCode(p) == '\n')
551       i++;
552   return(i);
553 }
554 \f
555 /*
556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
557 %                                                                             %
558 %                                                                             %
559 %                                                                             %
560 %   G e t M u l t i l i n e T y p e M e t r i c s                             %
561 %                                                                             %
562 %                                                                             %
563 %                                                                             %
564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
565 %
566 %  GetMultilineTypeMetrics() returns the following information for the
567 %  specified font and text:
568 %
569 %    character width
570 %    character height
571 %    ascender
572 %    descender
573 %    text width
574 %    text height
575 %    maximum horizontal advance
576 %    bounds: x1
577 %    bounds: y1
578 %    bounds: x2
579 %    bounds: y2
580 %    origin: x
581 %    origin: y
582 %    underline position
583 %    underline thickness
584 %
585 %  This method is like GetTypeMetrics() but it returns the maximum text width
586 %  and height for multiple lines of text.
587 %
588 %  The format of the GetMultilineTypeMetrics method is:
589 %
590 %      MagickBooleanType GetMultilineTypeMetrics(Image *image,
591 %        const DrawInfo *draw_info,TypeMetric *metrics)
592 %
593 %  A description of each parameter follows:
594 %
595 %    o image: the image.
596 %
597 %    o draw_info: the draw info.
598 %
599 %    o metrics: Return the font metrics in this structure.
600 %
601 */
602 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
603   const DrawInfo *draw_info,TypeMetric *metrics)
604 {
605   char
606     **textlist;
607
608   DrawInfo
609     *annotate_info;
610
611   MagickBooleanType
612     status;
613
614   register ssize_t
615     i;
616
617   TypeMetric
618     extent;
619
620   assert(image != (Image *) NULL);
621   assert(image->signature == MagickSignature);
622   if (image->debug != MagickFalse)
623     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
624   assert(draw_info != (DrawInfo *) NULL);
625   assert(draw_info->text != (char *) NULL);
626   assert(draw_info->signature == MagickSignature);
627   if (*draw_info->text == '\0')
628     return(MagickFalse);
629   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
630   annotate_info->text=DestroyString(annotate_info->text);
631   /*
632     Convert newlines to multiple lines of text.
633   */
634   textlist=StringToList(draw_info->text);
635   if (textlist == (char **) NULL)
636     return(MagickFalse);
637   annotate_info->render=MagickFalse;
638   (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
639   (void) ResetMagickMemory(&extent,0,sizeof(extent));
640   /*
641     Find the widest of the text lines.
642   */
643   annotate_info->text=textlist[0];
644   status=GetTypeMetrics(image,annotate_info,&extent);
645   *metrics=extent;
646   for (i=1; textlist[i] != (char *) NULL; i++)
647   {
648     annotate_info->text=textlist[i];
649     status=GetTypeMetrics(image,annotate_info,&extent);
650     if (extent.width > metrics->width)
651       *metrics=extent;
652   }
653   metrics->height=(double) (i*(size_t) (metrics->ascent-
654     metrics->descent+0.5)+(i-1)*draw_info->interline_spacing);
655   /*
656     Relinquish resources.
657   */
658   annotate_info->text=(char *) NULL;
659   annotate_info=DestroyDrawInfo(annotate_info);
660   for (i=0; textlist[i] != (char *) NULL; i++)
661     textlist[i]=DestroyString(textlist[i]);
662   textlist=(char **) RelinquishMagickMemory(textlist);
663   return(status);
664 }
665 \f
666 /*
667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668 %                                                                             %
669 %                                                                             %
670 %                                                                             %
671 %   G e t T y p e M e t r i c s                                               %
672 %                                                                             %
673 %                                                                             %
674 %                                                                             %
675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
676 %
677 %  GetTypeMetrics() returns the following information for the specified font
678 %  and text:
679 %
680 %    character width
681 %    character height
682 %    ascender
683 %    descender
684 %    text width
685 %    text height
686 %    maximum horizontal advance
687 %    bounds: x1
688 %    bounds: y1
689 %    bounds: x2
690 %    bounds: y2
691 %    origin: x
692 %    origin: y
693 %    underline position
694 %    underline thickness
695 %
696 %  The format of the GetTypeMetrics method is:
697 %
698 %      MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
699 %        TypeMetric *metrics)
700 %
701 %  A description of each parameter follows:
702 %
703 %    o image: the image.
704 %
705 %    o draw_info: the draw info.
706 %
707 %    o metrics: Return the font metrics in this structure.
708 %
709 */
710 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
711   const DrawInfo *draw_info,TypeMetric *metrics)
712 {
713   DrawInfo
714     *annotate_info;
715
716   MagickBooleanType
717     status;
718
719   PointInfo
720     offset;
721
722   assert(image != (Image *) NULL);
723   assert(image->signature == MagickSignature);
724   if (image->debug != MagickFalse)
725     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
726   assert(draw_info != (DrawInfo *) NULL);
727   assert(draw_info->text != (char *) NULL);
728   assert(draw_info->signature == MagickSignature);
729   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
730   annotate_info->render=MagickFalse;
731   (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
732   offset.x=0.0;
733   offset.y=0.0;
734   status=RenderType(image,annotate_info,&offset,metrics);
735   if (image->debug != MagickFalse)
736     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
737       "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
738       "bounds: %g,%g  %g,%g; origin: %g,%g; pixels per em: %g,%g; "
739       "underline position: %g; underline thickness: %g",annotate_info->text,
740       metrics->width,metrics->height,metrics->ascent,metrics->descent,
741       metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
742       metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
743       metrics->pixels_per_em.x,metrics->pixels_per_em.y,
744       metrics->underline_position,metrics->underline_thickness);
745   annotate_info=DestroyDrawInfo(annotate_info);
746   return(status);
747 }
748 \f
749 /*
750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751 %                                                                             %
752 %                                                                             %
753 %                                                                             %
754 +   R e n d e r T y p e                                                       %
755 %                                                                             %
756 %                                                                             %
757 %                                                                             %
758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759 %
760 %  RenderType() renders text on the image.  It also returns the bounding box of
761 %  the text relative to the image.
762 %
763 %  The format of the RenderType method is:
764 %
765 %      MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
766 %        const PointInfo *offset,TypeMetric *metrics)
767 %
768 %  A description of each parameter follows:
769 %
770 %    o image: the image.
771 %
772 %    o draw_info: the draw info.
773 %
774 %    o offset: (x,y) location of text relative to image.
775 %
776 %    o metrics: bounding box of text.
777 %
778 */
779 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
780   const PointInfo *offset,TypeMetric *metrics)
781 {
782   const TypeInfo
783     *type_info;
784
785   DrawInfo
786     *annotate_info;
787
788   MagickBooleanType
789     status;
790
791   type_info=(const TypeInfo *) NULL;
792   if (draw_info->font != (char *) NULL)
793     {
794       if (*draw_info->font == '@')
795         {
796           status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
797             metrics);
798           return(status);
799         }
800       if (*draw_info->font == '-')
801         return(RenderX11(image,draw_info,offset,metrics));
802       if (IsPathAccessible(draw_info->font) != MagickFalse)
803         {
804           status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
805             metrics);
806           return(status);
807         }
808       type_info=GetTypeInfo(draw_info->font,&image->exception);
809       if (type_info == (const TypeInfo *) NULL)
810         (void) ThrowMagickException(&image->exception,GetMagickModule(),
811           TypeWarning,"UnableToReadFont","`%s'",draw_info->font);
812     }
813   if ((type_info == (const TypeInfo *) NULL) &&
814       (draw_info->family != (const char *) NULL))
815     {
816       type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
817         draw_info->stretch,draw_info->weight,&image->exception);
818       if (type_info == (const TypeInfo *) NULL)
819         (void) ThrowMagickException(&image->exception,GetMagickModule(),
820           TypeWarning,"UnableToReadFont","`%s'",draw_info->family);
821     }
822   if (type_info == (const TypeInfo *) NULL)
823     type_info=GetTypeInfoByFamily("Arial",draw_info->style,
824       draw_info->stretch,draw_info->weight,&image->exception);
825   if (type_info == (const TypeInfo *) NULL)
826     type_info=GetTypeInfoByFamily("Helvetica",draw_info->style,
827       draw_info->stretch,draw_info->weight,&image->exception);
828   if (type_info == (const TypeInfo *) NULL)
829     type_info=GetTypeInfoByFamily("Century Schoolbook",draw_info->style,
830       draw_info->stretch,draw_info->weight,&image->exception);
831   if (type_info == (const TypeInfo *) NULL)
832     type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
833       draw_info->stretch,draw_info->weight,&image->exception);
834   if (type_info == (const TypeInfo *) NULL)
835     type_info=GetTypeInfo("*",&image->exception);
836   if (type_info == (const TypeInfo *) NULL)
837     {
838       status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics);
839       return(status);
840     }
841   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
842   annotate_info->face=type_info->face;
843   if (type_info->metrics != (char *) NULL)
844     (void) CloneString(&annotate_info->metrics,type_info->metrics);
845   if (type_info->glyphs != (char *) NULL)
846     (void) CloneString(&annotate_info->font,type_info->glyphs);
847   status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics);
848   annotate_info=DestroyDrawInfo(annotate_info);
849   return(status);
850 }
851 \f
852 /*
853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
854 %                                                                             %
855 %                                                                             %
856 %                                                                             %
857 +   R e n d e r F r e e t y p e                                               %
858 %                                                                             %
859 %                                                                             %
860 %                                                                             %
861 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
862 %
863 %  RenderFreetype() renders text on the image with a Truetype font.  It also
864 %  returns the bounding box of the text relative to the image.
865 %
866 %  The format of the RenderFreetype method is:
867 %
868 %      MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
869 %        const char *encoding,const PointInfo *offset,TypeMetric *metrics)
870 %
871 %  A description of each parameter follows:
872 %
873 %    o image: the image.
874 %
875 %    o draw_info: the draw info.
876 %
877 %    o encoding: the font encoding.
878 %
879 %    o offset: (x,y) location of text relative to image.
880 %
881 %    o metrics: bounding box of text.
882 %
883 */
884
885 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
886
887 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
888   DrawInfo *draw_info)
889 {
890   AffineMatrix
891     affine;
892
893   char
894     path[MaxTextExtent];
895
896   affine=draw_info->affine;
897   (void) FormatMagickString(path,MaxTextExtent,
898     "C%g,%g %g,%g %g,%g",affine.tx+p->x/64.0,affine.ty-
899     p->y/64.0,affine.tx+q->x/64.0,affine.ty-q->y/64.0,affine.tx+to->x/64.0,
900     affine.ty-to->y/64.0);
901   (void) ConcatenateString(&draw_info->primitive,path);
902   return(0);
903 }
904
905 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
906 {
907   AffineMatrix
908     affine;
909
910   char
911     path[MaxTextExtent];
912
913   affine=draw_info->affine;
914   (void) FormatMagickString(path,MaxTextExtent,"L%g,%g",affine.tx+
915     to->x/64.0,affine.ty-to->y/64.0);
916   (void) ConcatenateString(&draw_info->primitive,path);
917   return(0);
918 }
919
920 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
921 {
922   AffineMatrix
923     affine;
924
925   char
926     path[MaxTextExtent];
927
928   affine=draw_info->affine;
929   (void) FormatMagickString(path,MaxTextExtent,"M%g,%g",affine.tx+
930     to->x/64.0,affine.ty-to->y/64.0);
931   (void) ConcatenateString(&draw_info->primitive,path);
932   return(0);
933 }
934
935 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
936   DrawInfo *draw_info)
937 {
938   AffineMatrix
939     affine;
940
941   char
942     path[MaxTextExtent];
943
944   affine=draw_info->affine;
945   (void) FormatMagickString(path,MaxTextExtent,"Q%g,%g %g,%g",
946     affine.tx+control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,
947     affine.ty-to->y/64.0);
948   (void) ConcatenateString(&draw_info->primitive,path);
949   return(0);
950 }
951
952 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
953   const char *encoding,const PointInfo *offset,TypeMetric *metrics)
954 {
955 #if !defined(FT_OPEN_PATHNAME)
956 #define FT_OPEN_PATHNAME  ft_open_pathname
957 #endif
958
959   typedef struct _GlyphInfo
960   {
961     FT_UInt
962       id;
963
964     FT_Vector
965       origin;
966
967     FT_Glyph
968       image;
969   } GlyphInfo;
970
971   const char
972     *value;
973
974   double
975     direction;
976
977   DrawInfo
978     *annotate_info;
979
980   FT_BBox
981     bounds;
982
983   FT_BitmapGlyph
984     bitmap;
985
986   FT_Encoding
987     encoding_type;
988
989   FT_Error
990     status;
991
992   FT_Face
993     face;
994
995   FT_Int32
996     flags;
997
998   FT_Library
999     library;
1000
1001   FT_Matrix
1002     affine;
1003
1004   FT_Open_Args
1005     args;
1006
1007   FT_Vector
1008     origin;
1009
1010   GlyphInfo
1011     glyph,
1012     last_glyph;
1013
1014   ssize_t
1015     code,
1016     y;
1017
1018   PointInfo
1019     point,
1020     resolution;
1021
1022   register char
1023     *p;
1024
1025   static FT_Outline_Funcs
1026     OutlineMethods =
1027     {
1028       (FT_Outline_MoveTo_Func) TraceMoveTo,
1029       (FT_Outline_LineTo_Func) TraceLineTo,
1030       (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1031       (FT_Outline_CubicTo_Func) TraceCubicBezier,
1032       0, 0
1033     };
1034
1035   unsigned char
1036     *utf8;
1037
1038   /*
1039     Initialize Truetype library.
1040   */
1041   status=FT_Init_FreeType(&library);
1042   if (status != 0)
1043     ThrowBinaryException(TypeError,"UnableToInitializeFreetypeLibrary",
1044       image->filename);
1045   args.flags=FT_OPEN_PATHNAME;
1046   if (draw_info->font == (char *) NULL)
1047     args.pathname=ConstantString("helvetica");
1048   else
1049     if (*draw_info->font != '@')
1050       args.pathname=ConstantString(draw_info->font);
1051     else
1052       args.pathname=ConstantString(draw_info->font+1);
1053   face=(FT_Face) NULL;
1054   status=FT_Open_Face(library,&args,(long) draw_info->face,&face);
1055   args.pathname=DestroyString(args.pathname);
1056   if (status != 0)
1057     {
1058       (void) FT_Done_FreeType(library);
1059       (void) ThrowMagickException(&image->exception,GetMagickModule(),
1060         TypeError,"UnableToReadFont","`%s'",draw_info->font);
1061       return(RenderPostscript(image,draw_info,offset,metrics));
1062     }
1063   if ((draw_info->metrics != (char *) NULL) &&
1064       (IsPathAccessible(draw_info->metrics) != MagickFalse))
1065     (void) FT_Attach_File(face,draw_info->metrics);
1066   encoding_type=ft_encoding_unicode;
1067   status=FT_Select_Charmap(face,encoding_type);
1068   if ((status != 0) && (face->num_charmaps != 0))
1069     status=FT_Set_Charmap(face,face->charmaps[0]);
1070   if (encoding != (const char *) NULL)
1071     {
1072       if (LocaleCompare(encoding,"AdobeCustom") == 0)
1073         encoding_type=ft_encoding_adobe_custom;
1074       if (LocaleCompare(encoding,"AdobeExpert") == 0)
1075         encoding_type=ft_encoding_adobe_expert;
1076       if (LocaleCompare(encoding,"AdobeStandard") == 0)
1077         encoding_type=ft_encoding_adobe_standard;
1078       if (LocaleCompare(encoding,"AppleRoman") == 0)
1079         encoding_type=ft_encoding_apple_roman;
1080       if (LocaleCompare(encoding,"BIG5") == 0)
1081         encoding_type=ft_encoding_big5;
1082       if (LocaleCompare(encoding,"GB2312") == 0)
1083         encoding_type=ft_encoding_gb2312;
1084       if (LocaleCompare(encoding,"Johab") == 0)
1085         encoding_type=ft_encoding_johab;
1086 #if defined(ft_encoding_latin_1)
1087       if (LocaleCompare(encoding,"Latin-1") == 0)
1088         encoding_type=ft_encoding_latin_1;
1089 #endif
1090       if (LocaleCompare(encoding,"Latin-2") == 0)
1091         encoding_type=ft_encoding_latin_2;
1092       if (LocaleCompare(encoding,"None") == 0)
1093         encoding_type=ft_encoding_none;
1094       if (LocaleCompare(encoding,"SJIScode") == 0)
1095         encoding_type=ft_encoding_sjis;
1096       if (LocaleCompare(encoding,"Symbol") == 0)
1097         encoding_type=ft_encoding_symbol;
1098       if (LocaleCompare(encoding,"Unicode") == 0)
1099         encoding_type=ft_encoding_unicode;
1100       if (LocaleCompare(encoding,"Wansung") == 0)
1101         encoding_type=ft_encoding_wansung;
1102       status=FT_Select_Charmap(face,encoding_type);
1103       if (status != 0)
1104         ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
1105     }
1106   /*
1107     Set text size.
1108   */
1109   resolution.x=DefaultResolution;
1110   resolution.y=DefaultResolution;
1111   if (draw_info->density != (char *) NULL)
1112     {
1113       GeometryInfo
1114         geometry_info;
1115
1116       MagickStatusType
1117         flags;
1118
1119       flags=ParseGeometry(draw_info->density,&geometry_info);
1120       resolution.x=geometry_info.rho;
1121       resolution.y=geometry_info.sigma;
1122       if ((flags & SigmaValue) == 0)
1123         resolution.y=resolution.x;
1124     }
1125   status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1126     (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1127     (FT_UInt) resolution.y);
1128   metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1129   metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1130   metrics->ascent=(double) face->size->metrics.ascender/64.0;
1131   metrics->descent=(double) face->size->metrics.descender/64.0;
1132   metrics->width=0;
1133   metrics->origin.x=0;
1134   metrics->origin.y=0;
1135   metrics->height=(double) face->size->metrics.height/64.0;
1136   metrics->max_advance=0.0;
1137   if (face->size->metrics.max_advance > MagickEpsilon)
1138     metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1139   metrics->bounds.x1=0.0;
1140   metrics->bounds.y1=metrics->descent;
1141   metrics->bounds.x2=metrics->ascent+metrics->descent;
1142   metrics->bounds.y2=metrics->ascent+metrics->descent;
1143   metrics->underline_position=face->underline_position/64.0;
1144   metrics->underline_thickness=face->underline_thickness/64.0;
1145   if (*draw_info->text == '\0')
1146     {
1147       (void) FT_Done_Face(face);
1148       (void) FT_Done_FreeType(library);
1149       return(MagickTrue);
1150     }
1151   /*
1152     Compute bounding box.
1153   */
1154   if (image->debug != MagickFalse)
1155     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
1156       "font-encoding %s; text-encoding %s; pointsize %g",
1157       draw_info->font != (char *) NULL ? draw_info->font : "none",
1158       encoding != (char *) NULL ? encoding : "none",
1159       draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1160       draw_info->pointsize);
1161   flags=FT_LOAD_NO_BITMAP;
1162   value=GetImageProperty(image,"type:hinting");
1163   if ((value != (const char *) NULL) && (LocaleCompare(value,"off") == 0))
1164     flags|=FT_LOAD_NO_HINTING;
1165   glyph.id=0;
1166   glyph.image=NULL;
1167   last_glyph.id=0;
1168   last_glyph.image=NULL;
1169   origin.x=0;
1170   origin.y=0;
1171   affine.xx=65536L;
1172   affine.yx=0L;
1173   affine.xy=0L;
1174   affine.yy=65536L;
1175   if (draw_info->render != MagickFalse)
1176     {
1177       affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1178       affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1179       affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1180       affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1181     }
1182   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1183   (void) CloneString(&annotate_info->primitive,"path '");
1184   if (draw_info->render != MagickFalse)
1185     {
1186       if (image->storage_class != DirectClass)
1187         (void) SetImageStorageClass(image,DirectClass);
1188       if (image->matte == MagickFalse)
1189         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1190     }
1191   direction=1.0;
1192   if (draw_info->direction == RightToLeftDirection)
1193     direction=(-1.0);
1194   point.x=0.0;
1195   point.y=0.0;
1196   for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
1197     if (GetUTFCode(p) < 0)
1198       break;
1199   utf8=(unsigned char *) NULL;
1200   if (GetUTFCode(p) == 0)
1201     p=draw_info->text;
1202   else
1203     {
1204       utf8=ConvertLatin1ToUTF8((unsigned char *) draw_info->text);
1205       if (utf8 != (unsigned char *) NULL)
1206         p=(char *) utf8;
1207     }
1208   for (code=0; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
1209   {
1210     /*
1211       Render UTF-8 sequence.
1212     */
1213     glyph.id=FT_Get_Char_Index(face,GetUTFCode(p));
1214     if (glyph.id == 0)
1215       glyph.id=FT_Get_Char_Index(face,'?');
1216     if ((glyph.id != 0) && (last_glyph.id != 0))
1217       {
1218         if (draw_info->kerning != 0.0)
1219           origin.x+=64.0*direction*draw_info->kerning;
1220         else
1221           if (FT_HAS_KERNING(face))
1222             {
1223               FT_Vector
1224                 kerning;
1225
1226               status=FT_Get_Kerning(face,last_glyph.id,glyph.id,
1227                 ft_kerning_default,&kerning);
1228               if (status == 0)
1229                 origin.x+=direction*kerning.x;
1230             }
1231         }
1232     glyph.origin=origin;
1233     status=FT_Load_Glyph(face,glyph.id,flags);
1234     if (status != 0)
1235       continue;
1236     status=FT_Get_Glyph(face->glyph,&glyph.image);
1237     if (status != 0)
1238       continue;
1239     status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->outline,
1240       &bounds);
1241     if (status != 0)
1242       continue;
1243     if ((p == draw_info->text) || (bounds.xMin < metrics->bounds.x1))
1244       metrics->bounds.x1=bounds.xMin;
1245     if ((p == draw_info->text) || (bounds.yMin < metrics->bounds.y1))
1246       metrics->bounds.y1=bounds.yMin;
1247     if ((p == draw_info->text) || (bounds.xMax > metrics->bounds.x2))
1248       metrics->bounds.x2=bounds.xMax;
1249     if ((p == draw_info->text) || (bounds.yMax > metrics->bounds.y2))
1250       metrics->bounds.y2=bounds.yMax;
1251     if (draw_info->render != MagickFalse)
1252       if ((draw_info->stroke.opacity != TransparentOpacity) ||
1253           (draw_info->stroke_pattern != (Image *) NULL))
1254         {
1255           /*
1256             Trace the glyph.
1257           */
1258           annotate_info->affine.tx=glyph.origin.x/64.0;
1259           annotate_info->affine.ty=glyph.origin.y/64.0;
1260           (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->
1261             outline,&OutlineMethods,annotate_info);
1262         }
1263     FT_Vector_Transform(&glyph.origin,&affine);
1264     (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1265     status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
1266       (FT_Vector *) NULL,MagickTrue);
1267     if (status != 0)
1268       continue;
1269     bitmap=(FT_BitmapGlyph) glyph.image;
1270     point.x=offset->x+bitmap->left;
1271     point.y=offset->y-bitmap->top;
1272     if (draw_info->render != MagickFalse)
1273       {
1274         CacheView
1275           *image_view;
1276
1277         ExceptionInfo
1278           *exception;
1279
1280         MagickBooleanType
1281           status;
1282
1283         /*
1284           Rasterize the glyph.
1285         */
1286         status=MagickTrue;
1287         exception=(&image->exception);
1288         image_view=AcquireCacheView(image);
1289         for (y=0; y < (ssize_t) bitmap->bitmap.rows; y++)
1290         {
1291           MagickBooleanType
1292             active,
1293             sync;
1294
1295           MagickRealType
1296             fill_opacity;
1297
1298           PixelPacket
1299             fill_color;
1300
1301           register ssize_t
1302             x;
1303
1304           register PixelPacket
1305             *restrict q;
1306
1307           register unsigned char
1308             *p;
1309
1310           ssize_t
1311             x_offset,
1312             y_offset;
1313
1314           if (status == MagickFalse)
1315             continue;
1316           x_offset=(ssize_t) ceil(point.x-0.5);
1317           y_offset=(ssize_t) ceil(point.y+y-0.5);
1318           if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
1319             continue;
1320           q=(PixelPacket *) NULL;
1321           if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1322             active=MagickFalse;
1323           else
1324             {
1325               q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
1326                 bitmap->bitmap.width,1,exception);
1327               active=q != (PixelPacket *) NULL ? MagickTrue : MagickFalse;
1328             }
1329           p=bitmap->bitmap.buffer+y*bitmap->bitmap.width;
1330           for (x=0; x < (ssize_t) bitmap->bitmap.width; x++)
1331           {
1332             x_offset++;
1333             if ((*p == 0) || (x_offset < 0) ||
1334                 (x_offset >= (ssize_t) image->columns))
1335               {
1336                 p++;
1337                 q++;
1338                 continue;
1339               }
1340             fill_opacity=(MagickRealType) (*p)/(bitmap->bitmap.num_grays-1);
1341             if (draw_info->text_antialias == MagickFalse)
1342               fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
1343             if (active == MagickFalse)
1344               q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
1345                 exception);
1346             if (q == (PixelPacket *) NULL)
1347               {
1348                 p++;
1349                 q++;
1350                 continue;
1351               }
1352             (void) GetFillColor(draw_info,x_offset,y_offset,&fill_color);
1353             fill_opacity=QuantumRange-fill_opacity*(QuantumRange-
1354               fill_color.opacity);
1355             MagickCompositeOver(&fill_color,fill_opacity,q,q->opacity,q);
1356             if (active == MagickFalse)
1357               {
1358                 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1359                 if (sync == MagickFalse)
1360                   status=MagickFalse;
1361               }
1362             p++;
1363             q++;
1364           }
1365           sync=SyncCacheViewAuthenticPixels(image_view,exception);
1366           if (sync == MagickFalse)
1367             status=MagickFalse;
1368         }
1369         image_view=DestroyCacheView(image_view);
1370       }
1371     if ((bitmap->left+bitmap->bitmap.width) > metrics->width)
1372       metrics->width=bitmap->left+bitmap->bitmap.width;
1373     if ((draw_info->interword_spacing != 0.0) &&
1374         (IsUTFSpace(GetUTFCode(p)) != MagickFalse) &&
1375         (IsUTFSpace(code) == MagickFalse))
1376       origin.x+=64.0*direction*draw_info->interword_spacing;
1377     else
1378       origin.x+=direction*face->glyph->advance.x;
1379     metrics->origin.x=origin.x;
1380     metrics->origin.y=origin.y;
1381     if (last_glyph.id != 0)
1382       FT_Done_Glyph(last_glyph.image);
1383     last_glyph=glyph;
1384     code=GetUTFCode(p);
1385   }
1386   if (utf8 != (unsigned char *) NULL)
1387     utf8=(unsigned char *) RelinquishMagickMemory(utf8);
1388   if (last_glyph.id != 0)
1389     FT_Done_Glyph(last_glyph.image);
1390   if ((draw_info->stroke.opacity != TransparentOpacity) ||
1391       (draw_info->stroke_pattern != (Image *) NULL))
1392     {
1393       if (draw_info->render != MagickFalse)
1394         {
1395           /*
1396             Draw text stroke.
1397           */
1398           annotate_info->linejoin=RoundJoin;
1399           annotate_info->affine.tx=offset->x;
1400           annotate_info->affine.ty=offset->y;
1401           (void) ConcatenateString(&annotate_info->primitive,"'");
1402           (void) DrawImage(image,annotate_info);
1403         }
1404       }
1405   /*
1406     Determine font metrics.
1407   */
1408   glyph.id=FT_Get_Char_Index(face,'_');
1409   glyph.origin=origin;
1410   status=FT_Load_Glyph(face,glyph.id,flags);
1411   if (status == 0)
1412     {
1413       status=FT_Get_Glyph(face->glyph,&glyph.image);
1414       if (status == 0)
1415         {
1416           status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->
1417             outline,&bounds);
1418           if (status == 0)
1419             {
1420               FT_Vector_Transform(&glyph.origin,&affine);
1421               (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1422               status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
1423                 (FT_Vector *) NULL,MagickTrue);
1424               bitmap=(FT_BitmapGlyph) glyph.image;
1425               if (bitmap->left > metrics->width)
1426                 metrics->width=bitmap->left;
1427             }
1428         }
1429       if (glyph.id != 0)
1430         FT_Done_Glyph(glyph.image);
1431     }
1432   metrics->width-=metrics->bounds.x1/64.0;
1433   metrics->bounds.x1/=64.0;
1434   metrics->bounds.y1/=64.0;
1435   metrics->bounds.x2/=64.0;
1436   metrics->bounds.y2/=64.0;
1437   metrics->origin.x/=64.0;
1438   metrics->origin.y/=64.0;
1439   /*
1440     Relinquish resources.
1441   */
1442   annotate_info=DestroyDrawInfo(annotate_info);
1443   (void) FT_Done_Face(face);
1444   (void) FT_Done_FreeType(library);
1445   return(MagickTrue);
1446 }
1447 #else
1448 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1449   const char *magick_unused(encoding),const PointInfo *offset,
1450   TypeMetric *metrics)
1451 {
1452   (void) ThrowMagickException(&image->exception,GetMagickModule(),
1453     MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (Freetype)",
1454     draw_info->font);
1455   return(RenderPostscript(image,draw_info,offset,metrics));
1456 }
1457 #endif
1458 \f
1459 /*
1460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461 %                                                                             %
1462 %                                                                             %
1463 %                                                                             %
1464 +   R e n d e r P o s t s c r i p t                                           %
1465 %                                                                             %
1466 %                                                                             %
1467 %                                                                             %
1468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1469 %
1470 %  RenderPostscript() renders text on the image with a Postscript font.  It
1471 %  also returns the bounding box of the text relative to the image.
1472 %
1473 %  The format of the RenderPostscript method is:
1474 %
1475 %      MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
1476 %        const PointInfo *offset,TypeMetric *metrics)
1477 %
1478 %  A description of each parameter follows:
1479 %
1480 %    o image: the image.
1481 %
1482 %    o draw_info: the draw info.
1483 %
1484 %    o offset: (x,y) location of text relative to image.
1485 %
1486 %    o metrics: bounding box of text.
1487 %
1488 */
1489
1490 static inline size_t MagickMin(const size_t x,const size_t y)
1491 {
1492   if (x < y)
1493     return(x);
1494   return(y);
1495 }
1496
1497 static char *EscapeParenthesis(const char *text)
1498 {
1499   char
1500     *buffer;
1501
1502   register char
1503     *p;
1504
1505   register ssize_t
1506     i;
1507
1508   size_t
1509     escapes;
1510
1511   escapes=0;
1512   buffer=AcquireString(text);
1513   p=buffer;
1514   for (i=0; i < (ssize_t) MagickMin(strlen(text),MaxTextExtent-escapes-1); i++)
1515   {
1516     if ((text[i] == '(') || (text[i] == ')'))
1517       {
1518         *p++='\\';
1519         escapes++;
1520       }
1521     *p++=text[i];
1522   }
1523   *p='\0';
1524   return(buffer);
1525 }
1526
1527 static MagickBooleanType RenderPostscript(Image *image,
1528   const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics)
1529 {
1530   char
1531     filename[MaxTextExtent],
1532     geometry[MaxTextExtent],
1533     *text;
1534
1535   FILE
1536     *file;
1537
1538   Image
1539     *annotate_image;
1540
1541   ImageInfo
1542     *annotate_info;
1543
1544   int
1545     unique_file;
1546
1547   ssize_t
1548     y;
1549
1550   MagickBooleanType
1551     identity;
1552
1553   PointInfo
1554     extent,
1555     point,
1556     resolution;
1557
1558   register ssize_t
1559     i;
1560
1561   /*
1562     Render label with a Postscript font.
1563   */
1564   if (image->debug != MagickFalse)
1565     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1566       "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
1567       draw_info->font : "none",draw_info->pointsize);
1568   file=(FILE *) NULL;
1569   unique_file=AcquireUniqueFileResource(filename);
1570   if (unique_file != -1)
1571     file=fdopen(unique_file,"wb");
1572   if ((unique_file == -1) || (file == (FILE *) NULL))
1573     {
1574       ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
1575         filename);
1576       return(MagickFalse);
1577     }
1578   (void) fprintf(file,"%%!PS-Adobe-3.0\n");
1579   (void) fprintf(file,"/ReencodeType\n");
1580   (void) fprintf(file,"{\n");
1581   (void) fprintf(file,"  findfont dup length\n");
1582   (void) fprintf(file,
1583     "  dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
1584   (void) fprintf(file,
1585     "  /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
1586   (void) fprintf(file,"} bind def\n");
1587   /*
1588     Sample to compute bounding box.
1589   */
1590   identity=(draw_info->affine.sx == draw_info->affine.sy) &&
1591     (draw_info->affine.rx == 0.0) && (draw_info->affine.ry == 0.0) ?
1592     MagickTrue : MagickFalse;
1593   extent.x=0.0;
1594   extent.y=0.0;
1595   for (i=0; i <= (ssize_t) (strlen(draw_info->text)+2); i++)
1596   {
1597     point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
1598       draw_info->affine.ry*2.0*draw_info->pointsize);
1599     point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
1600       draw_info->affine.sy*2.0*draw_info->pointsize);
1601     if (point.x > extent.x)
1602       extent.x=point.x;
1603     if (point.y > extent.y)
1604       extent.y=point.y;
1605   }
1606   (void) fprintf(file,"%g %g moveto\n",identity  != MagickFalse ? 0.0 :
1607     extent.x/2.0,extent.y/2.0);
1608   (void) fprintf(file,"%g %g scale\n",draw_info->pointsize,
1609     draw_info->pointsize);
1610   if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
1611       (strchr(draw_info->font,'/') != (char *) NULL))
1612     (void) fprintf(file,
1613       "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
1614   else
1615     (void) fprintf(file,"/%s-ISO dup /%s ReencodeType findfont setfont\n",
1616       draw_info->font,draw_info->font);
1617   (void) fprintf(file,"[%g %g %g %g 0 0] concat\n",
1618     draw_info->affine.sx,-draw_info->affine.rx,-draw_info->affine.ry,
1619     draw_info->affine.sy);
1620   text=EscapeParenthesis(draw_info->text);
1621   if (identity == MagickFalse)
1622     (void) fprintf(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",text);
1623   (void) fprintf(file,"(%s) show\n",text);
1624   text=DestroyString(text);
1625   (void) fprintf(file,"showpage\n");
1626   (void) fclose(file);
1627   (void) FormatMagickString(geometry,MaxTextExtent,"%.20gx%.20g+0+0!",
1628     floor(extent.x+0.5),floor(extent.y+0.5));
1629   annotate_info=AcquireImageInfo();
1630   (void) FormatMagickString(annotate_info->filename,MaxTextExtent,"ps:%s",
1631     filename);
1632   (void) CloneString(&annotate_info->page,geometry);
1633   if (draw_info->density != (char *) NULL)
1634     (void) CloneString(&annotate_info->density,draw_info->density);
1635   annotate_info->antialias=draw_info->text_antialias;
1636   annotate_image=ReadImage(annotate_info,&image->exception);
1637   CatchException(&image->exception);
1638   annotate_info=DestroyImageInfo(annotate_info);
1639   (void) RelinquishUniqueFileResource(filename);
1640   if (annotate_image == (Image *) NULL)
1641     return(MagickFalse);
1642   resolution.x=DefaultResolution;
1643   resolution.y=DefaultResolution;
1644   if (draw_info->density != (char *) NULL)
1645     {
1646       GeometryInfo
1647         geometry_info;
1648
1649       MagickStatusType
1650         flags;
1651
1652       flags=ParseGeometry(draw_info->density,&geometry_info);
1653       resolution.x=geometry_info.rho;
1654       resolution.y=geometry_info.sigma;
1655       if ((flags & SigmaValue) == 0)
1656         resolution.y=resolution.x;
1657     }
1658   if (identity == MagickFalse)
1659     (void) TransformImage(&annotate_image,"0x0",(char *) NULL);
1660   else
1661     {
1662       RectangleInfo
1663         crop_info;
1664
1665       crop_info=GetImageBoundingBox(annotate_image,&annotate_image->exception);
1666       crop_info.height=(size_t) ((resolution.y/DefaultResolution)*
1667         ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
1668       crop_info.y=(ssize_t) ceil((resolution.y/DefaultResolution)*extent.y/8.0-
1669         0.5);
1670       (void) FormatMagickString(geometry,MaxTextExtent,
1671         "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
1672         crop_info.height,(double) crop_info.x,(double) crop_info.y);
1673       (void) TransformImage(&annotate_image,geometry,(char *) NULL);
1674     }
1675   metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
1676     ExpandAffine(&draw_info->affine)*draw_info->pointsize;
1677   metrics->pixels_per_em.y=metrics->pixels_per_em.x;
1678   metrics->ascent=metrics->pixels_per_em.x;
1679   metrics->descent=metrics->pixels_per_em.y/-5.0;
1680   metrics->width=(double) annotate_image->columns/
1681     ExpandAffine(&draw_info->affine);
1682   metrics->height=1.152*metrics->pixels_per_em.x;
1683   metrics->max_advance=metrics->pixels_per_em.x;
1684   metrics->bounds.x1=0.0;
1685   metrics->bounds.y1=metrics->descent;
1686   metrics->bounds.x2=metrics->ascent+metrics->descent;
1687   metrics->bounds.y2=metrics->ascent+metrics->descent;
1688   metrics->underline_position=(-2.0);
1689   metrics->underline_thickness=1.0;
1690   if (draw_info->render == MagickFalse)
1691     {
1692       annotate_image=DestroyImage(annotate_image);
1693       return(MagickTrue);
1694     }
1695   if (draw_info->fill.opacity != TransparentOpacity)
1696     {
1697       ExceptionInfo
1698         *exception;
1699
1700       MagickBooleanType
1701         sync;
1702
1703       PixelPacket
1704         fill_color;
1705
1706       CacheView
1707         *annotate_view;
1708
1709       /*
1710         Render fill color.
1711       */
1712       if (image->matte == MagickFalse)
1713         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1714       if (annotate_image->matte == MagickFalse)
1715         (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel);
1716       fill_color=draw_info->fill;
1717       exception=(&image->exception);
1718       annotate_view=AcquireCacheView(annotate_image);
1719       for (y=0; y < (ssize_t) annotate_image->rows; y++)
1720       {
1721         register ssize_t
1722           x;
1723
1724         register PixelPacket
1725           *restrict q;
1726
1727         q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
1728           1,exception);
1729         if (q == (PixelPacket *) NULL)
1730           break;
1731         for (x=0; x < (ssize_t) annotate_image->columns; x++)
1732         {
1733           (void) GetFillColor(draw_info,x,y,&fill_color);
1734           q->opacity=ClampToQuantum(QuantumRange-(((QuantumRange-
1735             (MagickRealType) PixelIntensityToQuantum(q))*(QuantumRange-
1736             fill_color.opacity))/QuantumRange));
1737           q->red=fill_color.red;
1738           q->green=fill_color.green;
1739           q->blue=fill_color.blue;
1740           q++;
1741         }
1742         sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
1743         if (sync == MagickFalse)
1744           break;
1745       }
1746       annotate_view=DestroyCacheView(annotate_view);
1747       (void) CompositeImage(image,OverCompositeOp,annotate_image,
1748         (ssize_t) ceil(offset->x-0.5),(ssize_t) ceil(offset->y-(metrics->ascent+
1749         metrics->descent)-0.5));
1750     }
1751   annotate_image=DestroyImage(annotate_image);
1752   return(MagickTrue);
1753 }
1754 \f
1755 /*
1756 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1757 %                                                                             %
1758 %                                                                             %
1759 %                                                                             %
1760 +   R e n d e r X 1 1                                                         %
1761 %                                                                             %
1762 %                                                                             %
1763 %                                                                             %
1764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1765 %
1766 %  RenderX11() renders text on the image with an X11 font.  It also returns the
1767 %  bounding box of the text relative to the image.
1768 %
1769 %  The format of the RenderX11 method is:
1770 %
1771 %      MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
1772 %        const PointInfo *offset,TypeMetric *metrics)
1773 %
1774 %  A description of each parameter follows:
1775 %
1776 %    o image: the image.
1777 %
1778 %    o draw_info: the draw info.
1779 %
1780 %    o offset: (x,y) location of text relative to image.
1781 %
1782 %    o metrics: bounding box of text.
1783 %
1784 */
1785 #if defined(MAGICKCORE_X11_DELEGATE)
1786 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
1787   const PointInfo *offset,TypeMetric *metrics)
1788 {
1789   MagickBooleanType
1790     status;
1791
1792   static DrawInfo
1793     cache_info;
1794
1795   static Display
1796     *display = (Display *) NULL;
1797
1798   static XAnnotateInfo
1799     annotate_info;
1800
1801   static XFontStruct
1802     *font_info;
1803
1804   static XPixelInfo
1805     pixel;
1806
1807   static XResourceInfo
1808     resource_info;
1809
1810   static XrmDatabase
1811     resource_database;
1812
1813   static XStandardColormap
1814     *map_info;
1815
1816   static XVisualInfo
1817     *visual_info;
1818
1819   size_t
1820     height,
1821     width;
1822
1823   if (display == (Display *) NULL)
1824     {
1825       const char
1826         *client_name;
1827
1828       ImageInfo
1829         *image_info;
1830
1831       /*
1832         Open X server connection.
1833       */
1834       display=XOpenDisplay(draw_info->server_name);
1835       if (display == (Display *) NULL)
1836         {
1837           ThrowXWindowException(XServerError,"UnableToOpenXServer",
1838             draw_info->server_name);
1839           return(MagickFalse);
1840         }
1841       /*
1842         Get user defaults from X resource database.
1843       */
1844       (void) XSetErrorHandler(XError);
1845       image_info=AcquireImageInfo();
1846       client_name=GetClientName();
1847       resource_database=XGetResourceDatabase(display,client_name);
1848       XGetResourceInfo(image_info,resource_database,client_name,&resource_info);
1849       resource_info.close_server=MagickFalse;
1850       resource_info.colormap=PrivateColormap;
1851       resource_info.font=AcquireString(draw_info->font);
1852       resource_info.background_color=AcquireString("#ffffffffffff");
1853       resource_info.foreground_color=AcquireString("#000000000000");
1854       map_info=XAllocStandardColormap();
1855       if (map_info == (XStandardColormap *) NULL)
1856         {
1857           ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
1858             image->filename);
1859           return(MagickFalse);
1860         }
1861       /*
1862         Initialize visual info.
1863       */
1864       visual_info=XBestVisualInfo(display,map_info,&resource_info);
1865       if (visual_info == (XVisualInfo *) NULL)
1866         {
1867           ThrowXWindowException(XServerError,"UnableToGetVisual",
1868             image->filename);
1869           return(MagickFalse);
1870         }
1871       map_info->colormap=(Colormap) NULL;
1872       pixel.pixels=(unsigned long *) NULL;
1873       /*
1874         Initialize Standard Colormap info.
1875       */
1876       XGetMapInfo(visual_info,XDefaultColormap(display,visual_info->screen),
1877         map_info);
1878       XGetPixelPacket(display,visual_info,map_info,&resource_info,
1879         (Image *) NULL,&pixel);
1880       pixel.annotate_context=XDefaultGC(display,visual_info->screen);
1881       /*
1882         Initialize font info.
1883       */
1884       font_info=XBestFont(display,&resource_info,MagickFalse);
1885       if (font_info == (XFontStruct *) NULL)
1886         {
1887           ThrowXWindowException(XServerError,"UnableToLoadFont",
1888             draw_info->font);
1889           return(MagickFalse);
1890         }
1891       if ((map_info == (XStandardColormap *) NULL) ||
1892           (visual_info == (XVisualInfo *) NULL) ||
1893           (font_info == (XFontStruct *) NULL))
1894         {
1895           XFreeResources(display,visual_info,map_info,&pixel,font_info,
1896             &resource_info,(XWindowInfo *) NULL);
1897           ThrowXWindowException(XServerError,"UnableToLoadFont",
1898             image->filename);
1899           return(MagickFalse);
1900         }
1901       cache_info=(*draw_info);
1902     }
1903   /*
1904     Initialize annotate info.
1905   */
1906   XGetAnnotateInfo(&annotate_info);
1907   annotate_info.stencil=ForegroundStencil;
1908   if (cache_info.font != draw_info->font)
1909     {
1910       /*
1911         Type name has changed.
1912       */
1913       (void) XFreeFont(display,font_info);
1914       (void) CloneString(&resource_info.font,draw_info->font);
1915       font_info=XBestFont(display,&resource_info,MagickFalse);
1916       if (font_info == (XFontStruct *) NULL)
1917         {
1918           ThrowXWindowException(XServerError,"UnableToLoadFont",
1919             draw_info->font);
1920           return(MagickFalse);
1921         }
1922     }
1923   if (image->debug != MagickFalse)
1924     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1925       "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
1926       draw_info->font : "none",draw_info->pointsize);
1927   cache_info=(*draw_info);
1928   annotate_info.font_info=font_info;
1929   annotate_info.text=(char *) draw_info->text;
1930   annotate_info.width=(unsigned int) XTextWidth(font_info,draw_info->text,
1931     (int) strlen(draw_info->text));
1932   annotate_info.height=(unsigned int) font_info->ascent+font_info->descent;
1933   metrics->pixels_per_em.x=(double) font_info->max_bounds.width;
1934   metrics->pixels_per_em.y=(double) font_info->ascent+font_info->descent;
1935   metrics->ascent=(double) font_info->ascent+4;
1936   metrics->descent=(double) (-font_info->descent);
1937   metrics->width=annotate_info.width/ExpandAffine(&draw_info->affine);
1938   metrics->height=font_info->ascent+font_info->descent;
1939   metrics->max_advance=(double) font_info->max_bounds.width;
1940   metrics->bounds.x1=0.0;
1941   metrics->bounds.y1=metrics->descent;
1942   metrics->bounds.x2=metrics->ascent+metrics->descent;
1943   metrics->bounds.y2=metrics->ascent+metrics->descent;
1944   metrics->underline_position=(-2.0);
1945   metrics->underline_thickness=1.0;
1946   if (draw_info->render == MagickFalse)
1947     return(MagickTrue);
1948   if (draw_info->fill.opacity == TransparentOpacity)
1949     return(MagickTrue);
1950   /*
1951     Render fill color.
1952   */
1953   width=annotate_info.width;
1954   height=annotate_info.height;
1955   if ((draw_info->affine.rx != 0.0) || (draw_info->affine.ry != 0.0))
1956     {
1957       if (((draw_info->affine.sx-draw_info->affine.sy) == 0.0) &&
1958           ((draw_info->affine.rx+draw_info->affine.ry) == 0.0))
1959         annotate_info.degrees=(180.0/MagickPI)*
1960           atan2(draw_info->affine.rx,draw_info->affine.sx);
1961     }
1962   (void) FormatMagickString(annotate_info.geometry,MaxTextExtent,
1963     "%.20gx%.20g%+.20g%+.20g",(double) width,(double) height,
1964     ceil(offset->x-0.5),ceil(offset->y-metrics->ascent-metrics->descent+
1965     draw_info->interline_spacing-0.5));
1966   pixel.pen_color.red=ScaleQuantumToShort(draw_info->fill.red);
1967   pixel.pen_color.green=ScaleQuantumToShort(draw_info->fill.green);
1968   pixel.pen_color.blue=ScaleQuantumToShort(draw_info->fill.blue);
1969   status=XAnnotateImage(display,&pixel,&annotate_info,image);
1970   if (status == 0)
1971     {
1972       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
1973         image->filename);
1974       return(MagickFalse);
1975     }
1976   return(MagickTrue);
1977 }
1978 #else
1979 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
1980   const PointInfo *offset,TypeMetric *metrics)
1981 {
1982   (void) draw_info;
1983   (void) offset;
1984   (void) metrics;
1985   (void) ThrowMagickException(&image->exception,GetMagickModule(),
1986     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
1987     image->filename);
1988   return(MagickFalse);
1989 }
1990 #endif