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