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