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