]> granicus.if.org Git - imagemagick/blob - magick/annotate.c
Remove previous geometry changes
[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-2010 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 long
185     i;
186
187   size_t
188     length;
189
190   TypeMetric
191     metrics;
192
193   unsigned long
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=(unsigned long) 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=(long) (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,%lu",metrics.origin.x,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 %      long FormatMagickCaption(Image *image,DrawInfo *draw_info,
464 %        TypeMetric *metrics,char **caption)
465 %
466 %  A description of each parameter follows.
467 %
468 %    o image:  The image.
469 %
470 %    o caption: the caption.
471 %
472 %    o draw_info: the draw info.
473 %
474 %    o metrics: Return the font metrics in this structure.
475 %
476 */
477 MagickExport long FormatMagickCaption(Image *image,DrawInfo *draw_info,
478   TypeMetric *metrics,char **caption)
479 {
480   MagickBooleanType
481     status;
482
483   register char
484     *p,
485     *q,
486     *s;
487
488   register long
489     i;
490
491   unsigned long
492     width;
493
494   q=draw_info->text;
495   s=(char *) NULL;
496   for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
497   {
498     if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
499       s=p;
500     for (i=0; i < (long) GetUTFOctets(p); i++)
501       *q++=(*(p+i));
502     *q='\0';
503     status=GetTypeMetrics(image,draw_info,metrics);
504     if (status == MagickFalse)
505       break;
506     width=(unsigned long) floor(metrics->width+0.5);
507     if (GetUTFCode(p) != '\n')
508       if (width <= image->columns)
509         continue;
510     if (s == (char *) NULL)
511       {
512         s=p;
513         while ((IsUTFSpace(GetUTFCode(s)) == MagickFalse) &&
514                (GetUTFCode(s) != 0))
515           s+=GetUTFOctets(s);
516       }
517     if (GetUTFCode(s) != 0)
518       {
519         *s='\n';
520         p=s;
521       }
522     else
523       {
524         char
525           *target;
526
527         long
528           n;
529
530         /*
531           No convenient line breaks-- insert newline.
532         */
533         target=AcquireString(*caption);
534         n=p-(*caption);
535         CopyMagickString(target,*caption,n+1);
536         ConcatenateMagickString(target,"\n",strlen(*caption)+1);
537         ConcatenateMagickString(target,p,strlen(*caption)+2);
538         (void) DestroyString(*caption);
539         *caption=target;
540         p=(*caption)+n;
541       }
542     s=(char *) NULL;
543     q=draw_info->text;
544   }
545   i=0;
546   for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
547     if (GetUTFCode(p) == '\n')
548       i++;
549   return(i);
550 }
551 \f
552 /*
553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554 %                                                                             %
555 %                                                                             %
556 %                                                                             %
557 %   G e t M u l t i l i n e T y p e M e t r i c s                             %
558 %                                                                             %
559 %                                                                             %
560 %                                                                             %
561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562 %
563 %  GetMultilineTypeMetrics() returns the following information for the
564 %  specified font and text:
565 %
566 %    character width
567 %    character height
568 %    ascender
569 %    descender
570 %    text width
571 %    text height
572 %    maximum horizontal advance
573 %    bounds: x1
574 %    bounds: y1
575 %    bounds: x2
576 %    bounds: y2
577 %    origin: x
578 %    origin: y
579 %    underline position
580 %    underline thickness
581 %
582 %  This method is like GetTypeMetrics() but it returns the maximum text width
583 %  and height for multiple lines of text.
584 %
585 %  The format of the GetMultilineTypeMetrics method is:
586 %
587 %      MagickBooleanType GetMultilineTypeMetrics(Image *image,
588 %        const DrawInfo *draw_info,TypeMetric *metrics)
589 %
590 %  A description of each parameter follows:
591 %
592 %    o image: the image.
593 %
594 %    o draw_info: the draw info.
595 %
596 %    o metrics: Return the font metrics in this structure.
597 %
598 */
599 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
600   const DrawInfo *draw_info,TypeMetric *metrics)
601 {
602   char
603     **textlist;
604
605   DrawInfo
606     *annotate_info;
607
608   MagickBooleanType
609     status;
610
611   register long
612     i;
613
614   TypeMetric
615     extent;
616
617   assert(image != (Image *) NULL);
618   assert(image->signature == MagickSignature);
619   if (image->debug != MagickFalse)
620     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
621   assert(draw_info != (DrawInfo *) NULL);
622   assert(draw_info->text != (char *) NULL);
623   assert(draw_info->signature == MagickSignature);
624   if (*draw_info->text == '\0')
625     return(MagickFalse);
626   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
627   annotate_info->text=DestroyString(annotate_info->text);
628   /*
629     Convert newlines to multiple lines of text.
630   */
631   textlist=StringToList(draw_info->text);
632   if (textlist == (char **) NULL)
633     return(MagickFalse);
634   annotate_info->render=MagickFalse;
635   (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
636   (void) ResetMagickMemory(&extent,0,sizeof(extent));
637   /*
638     Find the widest of the text lines.
639   */
640   annotate_info->text=textlist[0];
641   status=GetTypeMetrics(image,annotate_info,&extent);
642   *metrics=extent;
643   for (i=1; textlist[i] != (char *) NULL; i++)
644   {
645     annotate_info->text=textlist[i];
646     status=GetTypeMetrics(image,annotate_info,&extent);
647     if (extent.width > metrics->width)
648       *metrics=extent;
649   }
650   metrics->height=(double) (i*(unsigned long) (metrics->ascent-
651     metrics->descent+0.5)+(i-1)*draw_info->interline_spacing);
652   /*
653     Relinquish resources.
654   */
655   annotate_info->text=(char *) NULL;
656   annotate_info=DestroyDrawInfo(annotate_info);
657   for (i=0; textlist[i] != (char *) NULL; i++)
658     textlist[i]=DestroyString(textlist[i]);
659   textlist=(char **) RelinquishMagickMemory(textlist);
660   return(status);
661 }
662 \f
663 /*
664 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665 %                                                                             %
666 %                                                                             %
667 %                                                                             %
668 %   G e t T y p e M e t r i c s                                               %
669 %                                                                             %
670 %                                                                             %
671 %                                                                             %
672 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673 %
674 %  GetTypeMetrics() returns the following information for the specified font
675 %  and text:
676 %
677 %    character width
678 %    character height
679 %    ascender
680 %    descender
681 %    text width
682 %    text height
683 %    maximum horizontal advance
684 %    bounds: x1
685 %    bounds: y1
686 %    bounds: x2
687 %    bounds: y2
688 %    origin: x
689 %    origin: y
690 %    underline position
691 %    underline thickness
692 %
693 %  The format of the GetTypeMetrics method is:
694 %
695 %      MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
696 %        TypeMetric *metrics)
697 %
698 %  A description of each parameter follows:
699 %
700 %    o image: the image.
701 %
702 %    o draw_info: the draw info.
703 %
704 %    o metrics: Return the font metrics in this structure.
705 %
706 */
707 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
708   const DrawInfo *draw_info,TypeMetric *metrics)
709 {
710   DrawInfo
711     *annotate_info;
712
713   MagickBooleanType
714     status;
715
716   PointInfo
717     offset;
718
719   assert(image != (Image *) NULL);
720   assert(image->signature == MagickSignature);
721   if (image->debug != MagickFalse)
722     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
723   assert(draw_info != (DrawInfo *) NULL);
724   assert(draw_info->text != (char *) NULL);
725   assert(draw_info->signature == MagickSignature);
726   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
727   annotate_info->render=MagickFalse;
728   (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
729   offset.x=0.0;
730   offset.y=0.0;
731   status=RenderType(image,annotate_info,&offset,metrics);
732   if (image->debug != MagickFalse)
733     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
734       "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
735       "bounds: %g,%g  %g,%g; origin: %g,%g; pixels per em: %g,%g; "
736       "underline position: %g; underline thickness: %g",annotate_info->text,
737       metrics->width,metrics->height,metrics->ascent,metrics->descent,
738       metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
739       metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
740       metrics->pixels_per_em.x,metrics->pixels_per_em.y,
741       metrics->underline_position,metrics->underline_thickness);
742   annotate_info=DestroyDrawInfo(annotate_info);
743   return(status);
744 }
745 \f
746 /*
747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 %                                                                             %
749 %                                                                             %
750 %                                                                             %
751 +   R e n d e r T y p e                                                       %
752 %                                                                             %
753 %                                                                             %
754 %                                                                             %
755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756 %
757 %  RenderType() renders text on the image.  It also returns the bounding box of
758 %  the text relative to the image.
759 %
760 %  The format of the RenderType method is:
761 %
762 %      MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
763 %        const PointInfo *offset,TypeMetric *metrics)
764 %
765 %  A description of each parameter follows:
766 %
767 %    o image: the image.
768 %
769 %    o draw_info: the draw info.
770 %
771 %    o offset: (x,y) location of text relative to image.
772 %
773 %    o metrics: bounding box of text.
774 %
775 */
776 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
777   const PointInfo *offset,TypeMetric *metrics)
778 {
779   const TypeInfo
780     *type_info;
781
782   DrawInfo
783     *annotate_info;
784
785   MagickBooleanType
786     status;
787
788   type_info=(const TypeInfo *) NULL;
789   if (draw_info->font != (char *) NULL)
790     {
791       if (*draw_info->font == '@')
792         {
793           status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
794             metrics);
795           return(status);
796         }
797       if (*draw_info->font == '-')
798         return(RenderX11(image,draw_info,offset,metrics));
799       if (IsPathAccessible(draw_info->font) != MagickFalse)
800         {
801           status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
802             metrics);
803           return(status);
804         }
805       type_info=GetTypeInfo(draw_info->font,&image->exception);
806       if (type_info == (const TypeInfo *) NULL)
807         (void) ThrowMagickException(&image->exception,GetMagickModule(),
808           TypeWarning,"UnableToReadFont","`%s'",draw_info->font);
809     }
810   if ((type_info == (const TypeInfo *) NULL) &&
811       (draw_info->family != (const char *) NULL))
812     {
813       type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
814         draw_info->stretch,draw_info->weight,&image->exception);
815       if (type_info == (const TypeInfo *) NULL)
816         (void) ThrowMagickException(&image->exception,GetMagickModule(),
817           TypeWarning,"UnableToReadFont","`%s'",draw_info->family);
818     }
819   if (type_info == (const TypeInfo *) NULL)
820     type_info=GetTypeInfoByFamily("Arial",draw_info->style,
821       draw_info->stretch,draw_info->weight,&image->exception);
822   if (type_info == (const TypeInfo *) NULL)
823     type_info=GetTypeInfoByFamily("Helvetica",draw_info->style,
824       draw_info->stretch,draw_info->weight,&image->exception);
825   if (type_info == (const TypeInfo *) NULL)
826     type_info=GetTypeInfoByFamily("Century Schoolbook",draw_info->style,
827       draw_info->stretch,draw_info->weight,&image->exception);
828   if (type_info == (const TypeInfo *) NULL)
829     type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
830       draw_info->stretch,draw_info->weight,&image->exception);
831   if (type_info == (const TypeInfo *) NULL)
832     type_info=GetTypeInfo("*",&image->exception);
833   if (type_info == (const TypeInfo *) NULL)
834     {
835       status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics);
836       return(status);
837     }
838   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
839   annotate_info->face=type_info->face;
840   if (type_info->metrics != (char *) NULL)
841     (void) CloneString(&annotate_info->metrics,type_info->metrics);
842   if (type_info->glyphs != (char *) NULL)
843     (void) CloneString(&annotate_info->font,type_info->glyphs);
844   status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics);
845   annotate_info=DestroyDrawInfo(annotate_info);
846   return(status);
847 }
848 \f
849 /*
850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 %                                                                             %
852 %                                                                             %
853 %                                                                             %
854 +   R e n d e r F r e e t y p e                                               %
855 %                                                                             %
856 %                                                                             %
857 %                                                                             %
858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859 %
860 %  RenderFreetype() renders text on the image with a Truetype font.  It also
861 %  returns the bounding box of the text relative to the image.
862 %
863 %  The format of the RenderFreetype method is:
864 %
865 %      MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
866 %        const char *encoding,const PointInfo *offset,TypeMetric *metrics)
867 %
868 %  A description of each parameter follows:
869 %
870 %    o image: the image.
871 %
872 %    o draw_info: the draw info.
873 %
874 %    o encoding: the font encoding.
875 %
876 %    o offset: (x,y) location of text relative to image.
877 %
878 %    o metrics: bounding box of text.
879 %
880 */
881
882 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
883
884 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
885   DrawInfo *draw_info)
886 {
887   AffineMatrix
888     affine;
889
890   char
891     path[MaxTextExtent];
892
893   affine=draw_info->affine;
894   (void) FormatMagickString(path,MaxTextExtent,
895     "C%g,%g %g,%g %g,%g",affine.tx+p->x/64.0,affine.ty-
896     p->y/64.0,affine.tx+q->x/64.0,affine.ty-q->y/64.0,affine.tx+to->x/64.0,
897     affine.ty-to->y/64.0);
898   (void) ConcatenateString(&draw_info->primitive,path);
899   return(0);
900 }
901
902 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
903 {
904   AffineMatrix
905     affine;
906
907   char
908     path[MaxTextExtent];
909
910   affine=draw_info->affine;
911   (void) FormatMagickString(path,MaxTextExtent,"L%g,%g",affine.tx+
912     to->x/64.0,affine.ty-to->y/64.0);
913   (void) ConcatenateString(&draw_info->primitive,path);
914   return(0);
915 }
916
917 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
918 {
919   AffineMatrix
920     affine;
921
922   char
923     path[MaxTextExtent];
924
925   affine=draw_info->affine;
926   (void) FormatMagickString(path,MaxTextExtent,"M%g,%g",affine.tx+
927     to->x/64.0,affine.ty-to->y/64.0);
928   (void) ConcatenateString(&draw_info->primitive,path);
929   return(0);
930 }
931
932 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
933   DrawInfo *draw_info)
934 {
935   AffineMatrix
936     affine;
937
938   char
939     path[MaxTextExtent];
940
941   affine=draw_info->affine;
942   (void) FormatMagickString(path,MaxTextExtent,"Q%g,%g %g,%g",
943     affine.tx+control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,
944     affine.ty-to->y/64.0);
945   (void) ConcatenateString(&draw_info->primitive,path);
946   return(0);
947 }
948
949 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
950   const char *encoding,const PointInfo *offset,TypeMetric *metrics)
951 {
952 #if !defined(FT_OPEN_PATHNAME)
953 #define FT_OPEN_PATHNAME  ft_open_pathname
954 #endif
955
956   typedef struct _GlyphInfo
957   {
958     FT_UInt
959       id;
960
961     FT_Vector
962       origin;
963
964     FT_Glyph
965       image;
966   } GlyphInfo;
967
968   const char
969     *value;
970
971   double
972     direction;
973
974   DrawInfo
975     *annotate_info;
976
977   FT_BBox
978     bounds;
979
980   FT_BitmapGlyph
981     bitmap;
982
983   FT_Encoding
984     encoding_type;
985
986   FT_Error
987     status;
988
989   FT_Face
990     face;
991
992   FT_Int32
993     flags;
994
995   FT_Library
996     library;
997
998   FT_Matrix
999     affine;
1000
1001   FT_Open_Args
1002     args;
1003
1004   FT_Vector
1005     origin;
1006
1007   GlyphInfo
1008     glyph,
1009     last_glyph;
1010
1011   long
1012     code,
1013     y;
1014
1015   PointInfo
1016     point,
1017     resolution;
1018
1019   register char
1020     *p;
1021
1022   static FT_Outline_Funcs
1023     OutlineMethods =
1024     {
1025       (FT_Outline_MoveTo_Func) TraceMoveTo,
1026       (FT_Outline_LineTo_Func) TraceLineTo,
1027       (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1028       (FT_Outline_CubicTo_Func) TraceCubicBezier,
1029       0, 0
1030     };
1031
1032   unsigned char
1033     *utf8;
1034
1035   /*
1036     Initialize Truetype library.
1037   */
1038   status=FT_Init_FreeType(&library);
1039   if (status != 0)
1040     ThrowBinaryException(TypeError,"UnableToInitializeFreetypeLibrary",
1041       image->filename);
1042   args.flags=FT_OPEN_PATHNAME;
1043   if (draw_info->font == (char *) NULL)
1044     args.pathname=ConstantString("helvetica");
1045   else
1046     if (*draw_info->font != '@')
1047       args.pathname=ConstantString(draw_info->font);
1048     else
1049       args.pathname=ConstantString(draw_info->font+1);
1050   face=(FT_Face) NULL;
1051   status=FT_Open_Face(library,&args,draw_info->face,&face);
1052   args.pathname=DestroyString(args.pathname);
1053   if (status != 0)
1054     {
1055       (void) FT_Done_FreeType(library);
1056       (void) ThrowMagickException(&image->exception,GetMagickModule(),
1057         TypeError,"UnableToReadFont","`%s'",draw_info->font);
1058       return(RenderPostscript(image,draw_info,offset,metrics));
1059     }
1060   if ((draw_info->metrics != (char *) NULL) &&
1061       (IsPathAccessible(draw_info->metrics) != MagickFalse))
1062     (void) FT_Attach_File(face,draw_info->metrics);
1063   encoding_type=ft_encoding_unicode;
1064   status=FT_Select_Charmap(face,encoding_type);
1065   if ((status != 0) && (face->num_charmaps != 0))
1066     status=FT_Set_Charmap(face,face->charmaps[0]);
1067   if (encoding != (const char *) NULL)
1068     {
1069       if (LocaleCompare(encoding,"AdobeCustom") == 0)
1070         encoding_type=ft_encoding_adobe_custom;
1071       if (LocaleCompare(encoding,"AdobeExpert") == 0)
1072         encoding_type=ft_encoding_adobe_expert;
1073       if (LocaleCompare(encoding,"AdobeStandard") == 0)
1074         encoding_type=ft_encoding_adobe_standard;
1075       if (LocaleCompare(encoding,"AppleRoman") == 0)
1076         encoding_type=ft_encoding_apple_roman;
1077       if (LocaleCompare(encoding,"BIG5") == 0)
1078         encoding_type=ft_encoding_big5;
1079       if (LocaleCompare(encoding,"GB2312") == 0)
1080         encoding_type=ft_encoding_gb2312;
1081       if (LocaleCompare(encoding,"Johab") == 0)
1082         encoding_type=ft_encoding_johab;
1083 #if defined(ft_encoding_latin_1)
1084       if (LocaleCompare(encoding,"Latin-1") == 0)
1085         encoding_type=ft_encoding_latin_1;
1086 #endif
1087       if (LocaleCompare(encoding,"Latin-2") == 0)
1088         encoding_type=ft_encoding_latin_2;
1089       if (LocaleCompare(encoding,"None") == 0)
1090         encoding_type=ft_encoding_none;
1091       if (LocaleCompare(encoding,"SJIScode") == 0)
1092         encoding_type=ft_encoding_sjis;
1093       if (LocaleCompare(encoding,"Symbol") == 0)
1094         encoding_type=ft_encoding_symbol;
1095       if (LocaleCompare(encoding,"Unicode") == 0)
1096         encoding_type=ft_encoding_unicode;
1097       if (LocaleCompare(encoding,"Wansung") == 0)
1098         encoding_type=ft_encoding_wansung;
1099       status=FT_Select_Charmap(face,encoding_type);
1100       if (status != 0)
1101         ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
1102     }
1103   /*
1104     Set text size.
1105   */
1106   resolution.x=DefaultResolution;
1107   resolution.y=DefaultResolution;
1108   if (draw_info->density != (char *) NULL)
1109     {
1110       GeometryInfo
1111         geometry_info;
1112
1113       MagickStatusType
1114         flags;
1115
1116       flags=ParseGeometry(draw_info->density,&geometry_info);
1117       resolution.x=geometry_info.rho;
1118       resolution.y=geometry_info.sigma;
1119       if ((flags & SigmaValue) == 0)
1120         resolution.y=resolution.x;
1121     }
1122   status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1123     (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1124     (FT_UInt) resolution.y);
1125   metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1126   metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1127   metrics->ascent=(double) face->size->metrics.ascender/64.0;
1128   metrics->descent=(double) face->size->metrics.descender/64.0;
1129   metrics->width=0;
1130   metrics->origin.x=0;
1131   metrics->origin.y=0;
1132   metrics->height=(double) face->size->metrics.height/64.0;
1133   metrics->max_advance=0.0;
1134   if (face->size->metrics.max_advance > MagickEpsilon)
1135     metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1136   metrics->bounds.x1=0.0;
1137   metrics->bounds.y1=metrics->descent;
1138   metrics->bounds.x2=metrics->ascent+metrics->descent;
1139   metrics->bounds.y2=metrics->ascent+metrics->descent;
1140   metrics->underline_position=face->underline_position/64.0;
1141   metrics->underline_thickness=face->underline_thickness/64.0;
1142   if (*draw_info->text == '\0')
1143     {
1144       (void) FT_Done_Face(face);
1145       (void) FT_Done_FreeType(library);
1146       return(MagickTrue);
1147     }
1148   /*
1149     Compute bounding box.
1150   */
1151   if (image->debug != MagickFalse)
1152     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
1153       "font-encoding %s; text-encoding %s; pointsize %g",
1154       draw_info->font != (char *) NULL ? draw_info->font : "none",
1155       encoding != (char *) NULL ? encoding : "none",
1156       draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1157       draw_info->pointsize);
1158   flags=FT_LOAD_NO_BITMAP;
1159   value=GetImageProperty(image,"type:hinting");
1160   if (LocaleCompare(value,"off") == 0)
1161     flags|=FT_LOAD_NO_HINTING;
1162   glyph.id=0;
1163   glyph.image=NULL;
1164   last_glyph.id=0;
1165   last_glyph.image=NULL;
1166   origin.x=0;
1167   origin.y=0;
1168   affine.xx=65536L;
1169   affine.yx=0L;
1170   affine.xy=0L;
1171   affine.yy=65536L;
1172   if (draw_info->render != MagickFalse)
1173     {
1174       affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1175       affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1176       affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1177       affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1178     }
1179   annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1180   (void) CloneString(&annotate_info->primitive,"path '");
1181   if (draw_info->render != MagickFalse)
1182     {
1183       if (image->storage_class != DirectClass)
1184         (void) SetImageStorageClass(image,DirectClass);
1185       if (image->matte == MagickFalse)
1186         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1187     }
1188   direction=1.0;
1189   if (draw_info->direction == RightToLeftDirection)
1190     direction=(-1.0);
1191   point.x=0.0;
1192   point.y=0.0;
1193   for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
1194     if (GetUTFCode(p) < 0)
1195       break;
1196   utf8=(unsigned char *) NULL;
1197   if (GetUTFCode(p) == 0)
1198     p=draw_info->text;
1199   else
1200     {
1201       utf8=ConvertLatin1ToUTF8((unsigned char *) draw_info->text);
1202       if (utf8 != (unsigned char *) NULL)
1203         p=(char *) utf8;
1204     }
1205   for (code=0; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
1206   {
1207     /*
1208       Render UTF-8 sequence.
1209     */
1210     glyph.id=FT_Get_Char_Index(face,GetUTFCode(p));
1211     if (glyph.id == 0)
1212       glyph.id=FT_Get_Char_Index(face,'?');
1213     if ((glyph.id != 0) && (last_glyph.id != 0))
1214       {
1215         if (draw_info->kerning != 0.0)
1216           origin.x+=64.0*direction*draw_info->kerning;
1217         else
1218           if (FT_HAS_KERNING(face))
1219             {
1220               FT_Vector
1221                 kerning;
1222
1223               status=FT_Get_Kerning(face,last_glyph.id,glyph.id,
1224                 ft_kerning_default,&kerning);
1225               if (status == 0)
1226                 origin.x+=direction*kerning.x;
1227             }
1228         }
1229     glyph.origin=origin;
1230     status=FT_Load_Glyph(face,glyph.id,flags);
1231     if (status != 0)
1232       continue;
1233     status=FT_Get_Glyph(face->glyph,&glyph.image);
1234     if (status != 0)
1235       continue;
1236     status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->outline,
1237       &bounds);
1238     if (status != 0)
1239       continue;
1240     if ((p == draw_info->text) || (bounds.xMin < metrics->bounds.x1))
1241       metrics->bounds.x1=bounds.xMin;
1242     if ((p == draw_info->text) || (bounds.yMin < metrics->bounds.y1))
1243       metrics->bounds.y1=bounds.yMin;
1244     if ((p == draw_info->text) || (bounds.xMax > metrics->bounds.x2))
1245       metrics->bounds.x2=bounds.xMax;
1246     if ((p == draw_info->text) || (bounds.yMax > metrics->bounds.y2))
1247       metrics->bounds.y2=bounds.yMax;
1248     if (draw_info->render != MagickFalse)
1249       if ((draw_info->stroke.opacity != TransparentOpacity) ||
1250           (draw_info->stroke_pattern != (Image *) NULL))
1251         {
1252           /*
1253             Trace the glyph.
1254           */
1255           annotate_info->affine.tx=glyph.origin.x/64.0;
1256           annotate_info->affine.ty=glyph.origin.y/64.0;
1257           (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->
1258             outline,&OutlineMethods,annotate_info);
1259         }
1260     FT_Vector_Transform(&glyph.origin,&affine);
1261     (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1262     status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
1263       (FT_Vector *) NULL,MagickTrue);
1264     if (status != 0)
1265       continue;
1266     bitmap=(FT_BitmapGlyph) glyph.image;
1267     point.x=offset->x+bitmap->left;
1268     point.y=offset->y-bitmap->top;
1269     if (draw_info->render != MagickFalse)
1270       {
1271         CacheView
1272           *image_view;
1273
1274         ExceptionInfo
1275           *exception;
1276
1277         MagickBooleanType
1278           status;
1279
1280         /*
1281           Rasterize the glyph.
1282         */
1283         status=MagickTrue;
1284         exception=(&image->exception);
1285         image_view=AcquireCacheView(image);
1286         for (y=0; y < (long) bitmap->bitmap.rows; y++)
1287         {
1288           long
1289             x_offset,
1290             y_offset;
1291
1292           MagickBooleanType
1293             active,
1294             sync;
1295
1296           MagickRealType
1297             fill_opacity;
1298
1299           PixelPacket
1300             fill_color;
1301
1302           register long
1303             x;
1304
1305           register PixelPacket
1306             *restrict q;
1307
1308           register unsigned char
1309             *p;
1310
1311           if (status == MagickFalse)
1312             continue;
1313           x_offset=(long) ceil(point.x-0.5);
1314           y_offset=(long) ceil(point.y+y-0.5);
1315           if ((y_offset < 0) || (y_offset >= (long) image->rows))
1316             continue;
1317           q=(PixelPacket *) NULL;
1318           if ((x_offset < 0) || (x_offset >= (long) image->columns))
1319             active=MagickFalse;
1320           else
1321             {
1322               q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
1323                 bitmap->bitmap.width,1,exception);
1324               active=q != (PixelPacket *) NULL ? MagickTrue : MagickFalse;
1325             }
1326           p=bitmap->bitmap.buffer+y*bitmap->bitmap.width;
1327           for (x=0; x < (long) bitmap->bitmap.width; x++)
1328           {
1329             x_offset++;
1330             if ((*p == 0) || (x_offset < 0) ||
1331                 (x_offset >= (long) image->columns))
1332               {
1333                 p++;
1334                 q++;
1335                 continue;
1336               }
1337             fill_opacity=(MagickRealType) (*p)/(bitmap->bitmap.num_grays-1);
1338             if (draw_info->text_antialias == MagickFalse)
1339               fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
1340             if (active == MagickFalse)
1341               q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
1342                 exception);
1343             if (q == (PixelPacket *) NULL)
1344               {
1345                 p++;
1346                 q++;
1347                 continue;
1348               }
1349             (void) GetFillColor(draw_info,x_offset,y_offset,&fill_color);
1350             fill_opacity=QuantumRange-fill_opacity*(QuantumRange-
1351               fill_color.opacity);
1352             MagickCompositeOver(&fill_color,fill_opacity,q,q->opacity,q);
1353             if (active == MagickFalse)
1354               {
1355                 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1356                 if (sync == MagickFalse)
1357                   status=MagickFalse;
1358               }
1359             p++;
1360             q++;
1361           }
1362           sync=SyncCacheViewAuthenticPixels(image_view,exception);
1363           if (sync == MagickFalse)
1364             status=MagickFalse;
1365         }
1366         image_view=DestroyCacheView(image_view);
1367       }
1368     if ((bitmap->left+bitmap->bitmap.width) > metrics->width)
1369       metrics->width=bitmap->left+bitmap->bitmap.width;
1370     if ((draw_info->interword_spacing != 0.0) &&
1371         (IsUTFSpace(GetUTFCode(p)) != MagickFalse) &&
1372         (IsUTFSpace(code) == MagickFalse))
1373       origin.x+=64.0*direction*draw_info->interword_spacing;
1374     else
1375       origin.x+=direction*face->glyph->advance.x;
1376     metrics->origin.x=origin.x;
1377     metrics->origin.y=origin.y;
1378     if (last_glyph.id != 0)
1379       FT_Done_Glyph(last_glyph.image);
1380     last_glyph=glyph;
1381     code=GetUTFCode(p);
1382   }
1383   if (utf8 != (unsigned char *) NULL)
1384     utf8=(unsigned char *) RelinquishMagickMemory(utf8);
1385   if (last_glyph.id != 0)
1386     FT_Done_Glyph(last_glyph.image);
1387   if ((draw_info->stroke.opacity != TransparentOpacity) ||
1388       (draw_info->stroke_pattern != (Image *) NULL))
1389     {
1390       if (draw_info->render != MagickFalse)
1391         {
1392           /*
1393             Draw text stroke.
1394           */
1395           annotate_info->linejoin=RoundJoin;
1396           annotate_info->affine.tx=offset->x;
1397           annotate_info->affine.ty=offset->y;
1398           (void) ConcatenateString(&annotate_info->primitive,"'");
1399           (void) DrawImage(image,annotate_info);
1400         }
1401       }
1402   /*
1403     Determine font metrics.
1404   */
1405   glyph.id=FT_Get_Char_Index(face,'_');
1406   glyph.origin=origin;
1407   status=FT_Load_Glyph(face,glyph.id,flags);
1408   if (status == 0)
1409     {
1410       status=FT_Get_Glyph(face->glyph,&glyph.image);
1411       if (status == 0)
1412         {
1413           status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->
1414             outline,&bounds);
1415           if (status == 0)
1416             {
1417               FT_Vector_Transform(&glyph.origin,&affine);
1418               (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1419               status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
1420                 (FT_Vector *) NULL,MagickTrue);
1421               bitmap=(FT_BitmapGlyph) glyph.image;
1422               if (bitmap->left > metrics->width)
1423                 metrics->width=bitmap->left;
1424             }
1425         }
1426       if (glyph.id != 0)
1427         FT_Done_Glyph(glyph.image);
1428     }
1429   metrics->width-=metrics->bounds.x1/64.0;
1430   metrics->bounds.x1/=64.0;
1431   metrics->bounds.y1/=64.0;
1432   metrics->bounds.x2/=64.0;
1433   metrics->bounds.y2/=64.0;
1434   metrics->origin.x/=64.0;
1435   metrics->origin.y/=64.0;
1436   /*
1437     Relinquish resources.
1438   */
1439   annotate_info=DestroyDrawInfo(annotate_info);
1440   (void) FT_Done_Face(face);
1441   (void) FT_Done_FreeType(library);
1442   return(MagickTrue);
1443 }
1444 #else
1445 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1446   const char *magick_unused(encoding),const PointInfo *offset,
1447   TypeMetric *metrics)
1448 {
1449   (void) ThrowMagickException(&image->exception,GetMagickModule(),
1450     MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (Freetype)",
1451     draw_info->font);
1452   return(RenderPostscript(image,draw_info,offset,metrics));
1453 }
1454 #endif
1455 \f
1456 /*
1457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458 %                                                                             %
1459 %                                                                             %
1460 %                                                                             %
1461 +   R e n d e r P o s t s c r i p t                                           %
1462 %                                                                             %
1463 %                                                                             %
1464 %                                                                             %
1465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466 %
1467 %  RenderPostscript() renders text on the image with a Postscript font.  It
1468 %  also returns the bounding box of the text relative to the image.
1469 %
1470 %  The format of the RenderPostscript method is:
1471 %
1472 %      MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
1473 %        const PointInfo *offset,TypeMetric *metrics)
1474 %
1475 %  A description of each parameter follows:
1476 %
1477 %    o image: the image.
1478 %
1479 %    o draw_info: the draw info.
1480 %
1481 %    o offset: (x,y) location of text relative to image.
1482 %
1483 %    o metrics: bounding box of text.
1484 %
1485 */
1486
1487 static inline size_t MagickMin(const size_t x,const size_t y)
1488 {
1489   if (x < y)
1490     return(x);
1491   return(y);
1492 }
1493
1494 static char *EscapeParenthesis(const char *text)
1495 {
1496   char
1497     *buffer;
1498
1499   register char
1500     *p;
1501
1502   register long
1503     i;
1504
1505   size_t
1506     escapes;
1507
1508   escapes=0;
1509   buffer=AcquireString(text);
1510   p=buffer;
1511   for (i=0; i < (long) MagickMin(strlen(text),MaxTextExtent-escapes-1); i++)
1512   {
1513     if ((text[i] == '(') || (text[i] == ')'))
1514       {
1515         *p++='\\';
1516         escapes++;
1517       }
1518     *p++=text[i];
1519   }
1520   *p='\0';
1521   return(buffer);
1522 }
1523
1524 static MagickBooleanType RenderPostscript(Image *image,
1525   const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics)
1526 {
1527   char
1528     filename[MaxTextExtent],
1529     geometry[MaxTextExtent],
1530     *text;
1531
1532   FILE
1533     *file;
1534
1535   Image
1536     *annotate_image;
1537
1538   ImageInfo
1539     *annotate_info;
1540
1541   int
1542     unique_file;
1543
1544   long
1545     y;
1546
1547   MagickBooleanType
1548     identity;
1549
1550   PointInfo
1551     extent,
1552     point,
1553     resolution;
1554
1555   register long
1556     i;
1557
1558   /*
1559     Render label with a Postscript font.
1560   */
1561   if (image->debug != MagickFalse)
1562     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1563       "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
1564       draw_info->font : "none",draw_info->pointsize);
1565   file=(FILE *) NULL;
1566   unique_file=AcquireUniqueFileResource(filename);
1567   if (unique_file != -1)
1568     file=fdopen(unique_file,"wb");
1569   if ((unique_file == -1) || (file == (FILE *) NULL))
1570     {
1571       ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
1572         filename);
1573       return(MagickFalse);
1574     }
1575   (void) fprintf(file,"%%!PS-Adobe-3.0\n");
1576   (void) fprintf(file,"/ReencodeType\n");
1577   (void) fprintf(file,"{\n");
1578   (void) fprintf(file,"  findfont dup length\n");
1579   (void) fprintf(file,
1580     "  dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
1581   (void) fprintf(file,
1582     "  /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
1583   (void) fprintf(file,"} bind def\n");
1584   /*
1585     Sample to compute bounding box.
1586   */
1587   identity=(draw_info->affine.sx == draw_info->affine.sy) &&
1588     (draw_info->affine.rx == 0.0) && (draw_info->affine.ry == 0.0) ?
1589     MagickTrue : MagickFalse;
1590   extent.x=0.0;
1591   extent.y=0.0;
1592   for (i=0; i <= (long) (strlen(draw_info->text)+2); i++)
1593   {
1594     point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
1595       draw_info->affine.ry*2.0*draw_info->pointsize);
1596     point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
1597       draw_info->affine.sy*2.0*draw_info->pointsize);
1598     if (point.x > extent.x)
1599       extent.x=point.x;
1600     if (point.y > extent.y)
1601       extent.y=point.y;
1602   }
1603   (void) fprintf(file,"%g %g moveto\n",identity  != MagickFalse ? 0.0 :
1604     extent.x/2.0,extent.y/2.0);
1605   (void) fprintf(file,"%g %g scale\n",draw_info->pointsize,
1606     draw_info->pointsize);
1607   if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
1608       (strchr(draw_info->font,'/') != (char *) NULL))
1609     (void) fprintf(file,
1610       "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
1611   else
1612     (void) fprintf(file,"/%s-ISO dup /%s ReencodeType findfont setfont\n",
1613       draw_info->font,draw_info->font);
1614   (void) fprintf(file,"[%g %g %g %g 0 0] concat\n",
1615     draw_info->affine.sx,-draw_info->affine.rx,-draw_info->affine.ry,
1616     draw_info->affine.sy);
1617   text=EscapeParenthesis(draw_info->text);
1618   if (identity == MagickFalse)
1619     (void) fprintf(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",text);
1620   (void) fprintf(file,"(%s) show\n",text);
1621   text=DestroyString(text);
1622   (void) fprintf(file,"showpage\n");
1623   (void) fclose(file);
1624   (void) FormatMagickString(geometry,MaxTextExtent,"%ldx%ld+0+0!",(long)
1625     floor(extent.x+0.5),(long) floor(extent.y+0.5));
1626   annotate_info=AcquireImageInfo();
1627   (void) FormatMagickString(annotate_info->filename,MaxTextExtent,"ps:%s",
1628     filename);
1629   (void) CloneString(&annotate_info->page,geometry);
1630   if (draw_info->density != (char *) NULL)
1631     (void) CloneString(&annotate_info->density,draw_info->density);
1632   annotate_info->antialias=draw_info->text_antialias;
1633   annotate_image=ReadImage(annotate_info,&image->exception);
1634   CatchException(&image->exception);
1635   annotate_info=DestroyImageInfo(annotate_info);
1636   (void) RelinquishUniqueFileResource(filename);
1637   if (annotate_image == (Image *) NULL)
1638     return(MagickFalse);
1639   resolution.x=DefaultResolution;
1640   resolution.y=DefaultResolution;
1641   if (draw_info->density != (char *) NULL)
1642     {
1643       GeometryInfo
1644         geometry_info;
1645
1646       MagickStatusType
1647         flags;
1648
1649       flags=ParseGeometry(draw_info->density,&geometry_info);
1650       resolution.x=geometry_info.rho;
1651       resolution.y=geometry_info.sigma;
1652       if ((flags & SigmaValue) == 0)
1653         resolution.y=resolution.x;
1654     }
1655   if (identity == MagickFalse)
1656     (void) TransformImage(&annotate_image,"0x0",(char *) NULL);
1657   else
1658     {
1659       RectangleInfo
1660         crop_info;
1661
1662       crop_info=GetImageBoundingBox(annotate_image,&annotate_image->exception);
1663       crop_info.height=(unsigned long) ((resolution.y/DefaultResolution)*
1664         ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
1665       crop_info.y=(long) ceil((resolution.y/DefaultResolution)*extent.y/8.0-
1666         0.5);
1667       (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu%+ld%+ld",
1668         crop_info.width,crop_info.height,crop_info.x,crop_info.y);
1669       (void) TransformImage(&annotate_image,geometry,(char *) NULL);
1670     }
1671   metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
1672     ExpandAffine(&draw_info->affine)*draw_info->pointsize;
1673   metrics->pixels_per_em.y=metrics->pixels_per_em.x;
1674   metrics->ascent=metrics->pixels_per_em.x;
1675   metrics->descent=metrics->pixels_per_em.y/-5.0;
1676   metrics->width=(double) annotate_image->columns/
1677     ExpandAffine(&draw_info->affine);
1678   metrics->height=1.152*metrics->pixels_per_em.x;
1679   metrics->max_advance=metrics->pixels_per_em.x;
1680   metrics->bounds.x1=0.0;
1681   metrics->bounds.y1=metrics->descent;
1682   metrics->bounds.x2=metrics->ascent+metrics->descent;
1683   metrics->bounds.y2=metrics->ascent+metrics->descent;
1684   metrics->underline_position=(-2.0);
1685   metrics->underline_thickness=1.0;
1686   if (draw_info->render == MagickFalse)
1687     {
1688       annotate_image=DestroyImage(annotate_image);
1689       return(MagickTrue);
1690     }
1691   if (draw_info->fill.opacity != TransparentOpacity)
1692     {
1693       ExceptionInfo
1694         *exception;
1695
1696       MagickBooleanType
1697         sync;
1698
1699       PixelPacket
1700         fill_color;
1701
1702       CacheView
1703         *annotate_view;
1704
1705       /*
1706         Render fill color.
1707       */
1708       if (image->matte == MagickFalse)
1709         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1710       if (annotate_image->matte == MagickFalse)
1711         (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel);
1712       fill_color=draw_info->fill;
1713       exception=(&image->exception);
1714       annotate_view=AcquireCacheView(annotate_image);
1715       for (y=0; y < (long) annotate_image->rows; y++)
1716       {
1717         register long
1718           x;
1719
1720         register PixelPacket
1721           *restrict q;
1722
1723         q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
1724           1,exception);
1725         if (q == (PixelPacket *) NULL)
1726           break;
1727         for (x=0; x < (long) annotate_image->columns; x++)
1728         {
1729           (void) GetFillColor(draw_info,x,y,&fill_color);
1730           q->opacity=ClampToQuantum(QuantumRange-(((QuantumRange-
1731             (MagickRealType) PixelIntensityToQuantum(q))*(QuantumRange-
1732             fill_color.opacity))/QuantumRange));
1733           q->red=fill_color.red;
1734           q->green=fill_color.green;
1735           q->blue=fill_color.blue;
1736           q++;
1737         }
1738         sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
1739         if (sync == MagickFalse)
1740           break;
1741       }
1742       annotate_view=DestroyCacheView(annotate_view);
1743       (void) CompositeImage(image,OverCompositeOp,annotate_image,
1744         (long) ceil(offset->x-0.5),(long) ceil(offset->y-(metrics->ascent+
1745         metrics->descent)-0.5));
1746     }
1747   annotate_image=DestroyImage(annotate_image);
1748   return(MagickTrue);
1749 }
1750 \f
1751 /*
1752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753 %                                                                             %
1754 %                                                                             %
1755 %                                                                             %
1756 +   R e n d e r X 1 1                                                         %
1757 %                                                                             %
1758 %                                                                             %
1759 %                                                                             %
1760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1761 %
1762 %  RenderX11() renders text on the image with an X11 font.  It also returns the
1763 %  bounding box of the text relative to the image.
1764 %
1765 %  The format of the RenderX11 method is:
1766 %
1767 %      MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
1768 %        const PointInfo *offset,TypeMetric *metrics)
1769 %
1770 %  A description of each parameter follows:
1771 %
1772 %    o image: the image.
1773 %
1774 %    o draw_info: the draw info.
1775 %
1776 %    o offset: (x,y) location of text relative to image.
1777 %
1778 %    o metrics: bounding box of text.
1779 %
1780 */
1781 #if defined(MAGICKCORE_X11_DELEGATE)
1782 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
1783   const PointInfo *offset,TypeMetric *metrics)
1784 {
1785   MagickBooleanType
1786     status;
1787
1788   static DrawInfo
1789     cache_info;
1790
1791   static Display
1792     *display = (Display *) NULL;
1793
1794   static XAnnotateInfo
1795     annotate_info;
1796
1797   static XFontStruct
1798     *font_info;
1799
1800   static XPixelInfo
1801     pixel;
1802
1803   static XResourceInfo
1804     resource_info;
1805
1806   static XrmDatabase
1807     resource_database;
1808
1809   static XStandardColormap
1810     *map_info;
1811
1812   static XVisualInfo
1813     *visual_info;
1814
1815   unsigned long
1816     height,
1817     width;
1818
1819   if (display == (Display *) NULL)
1820     {
1821       const char
1822         *client_name;
1823
1824       ImageInfo
1825         *image_info;
1826
1827       /*
1828         Open X server connection.
1829       */
1830       display=XOpenDisplay(draw_info->server_name);
1831       if (display == (Display *) NULL)
1832         {
1833           ThrowXWindowException(XServerError,"UnableToOpenXServer",
1834             draw_info->server_name);
1835           return(MagickFalse);
1836         }
1837       /*
1838         Get user defaults from X resource database.
1839       */
1840       (void) XSetErrorHandler(XError);
1841       image_info=AcquireImageInfo();
1842       client_name=GetClientName();
1843       resource_database=XGetResourceDatabase(display,client_name);
1844       XGetResourceInfo(image_info,resource_database,client_name,&resource_info);
1845       resource_info.close_server=MagickFalse;
1846       resource_info.colormap=PrivateColormap;
1847       resource_info.font=AcquireString(draw_info->font);
1848       resource_info.background_color=AcquireString("#ffffffffffff");
1849       resource_info.foreground_color=AcquireString("#000000000000");
1850       map_info=XAllocStandardColormap();
1851       if (map_info == (XStandardColormap *) NULL)
1852         {
1853           ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
1854             image->filename);
1855           return(MagickFalse);
1856         }
1857       /*
1858         Initialize visual info.
1859       */
1860       visual_info=XBestVisualInfo(display,map_info,&resource_info);
1861       if (visual_info == (XVisualInfo *) NULL)
1862         {
1863           ThrowXWindowException(XServerError,"UnableToGetVisual",
1864             image->filename);
1865           return(MagickFalse);
1866         }
1867       map_info->colormap=(Colormap) NULL;
1868       pixel.pixels=(unsigned long *) NULL;
1869       /*
1870         Initialize Standard Colormap info.
1871       */
1872       XGetMapInfo(visual_info,XDefaultColormap(display,visual_info->screen),
1873         map_info);
1874       XGetPixelPacket(display,visual_info,map_info,&resource_info,
1875         (Image *) NULL,&pixel);
1876       pixel.annotate_context=XDefaultGC(display,visual_info->screen);
1877       /*
1878         Initialize font info.
1879       */
1880       font_info=XBestFont(display,&resource_info,MagickFalse);
1881       if (font_info == (XFontStruct *) NULL)
1882         {
1883           ThrowXWindowException(XServerError,"UnableToLoadFont",
1884             draw_info->font);
1885           return(MagickFalse);
1886         }
1887       if ((map_info == (XStandardColormap *) NULL) ||
1888           (visual_info == (XVisualInfo *) NULL) ||
1889           (font_info == (XFontStruct *) NULL))
1890         {
1891           XFreeResources(display,visual_info,map_info,&pixel,font_info,
1892             &resource_info,(XWindowInfo *) NULL);
1893           ThrowXWindowException(XServerError,"UnableToLoadFont",
1894             image->filename);
1895           return(MagickFalse);
1896         }
1897       cache_info=(*draw_info);
1898     }
1899   /*
1900     Initialize annotate info.
1901   */
1902   XGetAnnotateInfo(&annotate_info);
1903   annotate_info.stencil=ForegroundStencil;
1904   if (cache_info.font != draw_info->font)
1905     {
1906       /*
1907         Type name has changed.
1908       */
1909       (void) XFreeFont(display,font_info);
1910       (void) CloneString(&resource_info.font,draw_info->font);
1911       font_info=XBestFont(display,&resource_info,MagickFalse);
1912       if (font_info == (XFontStruct *) NULL)
1913         {
1914           ThrowXWindowException(XServerError,"UnableToLoadFont",
1915             draw_info->font);
1916           return(MagickFalse);
1917         }
1918     }
1919   if (image->debug != MagickFalse)
1920     (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1921       "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
1922       draw_info->font : "none",draw_info->pointsize);
1923   cache_info=(*draw_info);
1924   annotate_info.font_info=font_info;
1925   annotate_info.text=(char *) draw_info->text;
1926   annotate_info.width=(unsigned int) XTextWidth(font_info,draw_info->text,
1927     (int) strlen(draw_info->text));
1928   annotate_info.height=(unsigned int) font_info->ascent+font_info->descent;
1929   metrics->pixels_per_em.x=(double) font_info->max_bounds.width;
1930   metrics->pixels_per_em.y=(double) font_info->ascent+font_info->descent;
1931   metrics->ascent=(double) font_info->ascent+4;
1932   metrics->descent=(double) (-font_info->descent);
1933   metrics->width=annotate_info.width/ExpandAffine(&draw_info->affine);
1934   metrics->height=font_info->ascent+font_info->descent;
1935   metrics->max_advance=(double) font_info->max_bounds.width;
1936   metrics->bounds.x1=0.0;
1937   metrics->bounds.y1=metrics->descent;
1938   metrics->bounds.x2=metrics->ascent+metrics->descent;
1939   metrics->bounds.y2=metrics->ascent+metrics->descent;
1940   metrics->underline_position=(-2.0);
1941   metrics->underline_thickness=1.0;
1942   if (draw_info->render == MagickFalse)
1943     return(MagickTrue);
1944   if (draw_info->fill.opacity == TransparentOpacity)
1945     return(MagickTrue);
1946   /*
1947     Render fill color.
1948   */
1949   width=annotate_info.width;
1950   height=annotate_info.height;
1951   if ((draw_info->affine.rx != 0.0) || (draw_info->affine.ry != 0.0))
1952     {
1953       if (((draw_info->affine.sx-draw_info->affine.sy) == 0.0) &&
1954           ((draw_info->affine.rx+draw_info->affine.ry) == 0.0))
1955         annotate_info.degrees=(180.0/MagickPI)*
1956           atan2(draw_info->affine.rx,draw_info->affine.sx);
1957     }
1958   (void) FormatMagickString(annotate_info.geometry,MaxTextExtent,
1959     "%lux%lu+%ld+%ld",width,height,(long) ceil(offset->x-0.5),
1960     (long) ceil(offset->y-metrics->ascent-metrics->descent+
1961     draw_info->interline_spacing-0.5));
1962   pixel.pen_color.red=ScaleQuantumToShort(draw_info->fill.red);
1963   pixel.pen_color.green=ScaleQuantumToShort(draw_info->fill.green);
1964   pixel.pen_color.blue=ScaleQuantumToShort(draw_info->fill.blue);
1965   status=XAnnotateImage(display,&pixel,&annotate_info,image);
1966   if (status == 0)
1967     {
1968       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
1969         image->filename);
1970       return(MagickFalse);
1971     }
1972   return(MagickTrue);
1973 }
1974 #else
1975 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
1976   const PointInfo *offset,TypeMetric *metrics)
1977 {
1978   (void) draw_info;
1979   (void) offset;
1980   (void) metrics;
1981   (void) ThrowMagickException(&image->exception,GetMagickModule(),
1982     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
1983     image->filename);
1984   return(MagickFalse);
1985 }
1986 #endif