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