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