root/ImageMagick/trunk/magick/annotate.c

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