MagickCore  7.1.2-29
Convert, Edit, Or Compose Bitmap Images
annotate.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % AAA N N N N OOO TTTTT AAA TTTTT EEEEE %
7 % A A NN N NN N O O T A A T E %
8 % AAAAA N N N N N N O O T AAAAA T EEE %
9 % A A N NN N NN O O T A A T E %
10 % A A N N N N OOO T A A T EEEEE %
11 % %
12 % %
13 % MagickCore Image Annotation Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/license/ %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Digital Applications (www.digapp.com) contributed the stroked text algorithm.
37 % It was written by Leonard Rosenthol.
38 %
39 %
40 */
41 ␌
42 /*
43  Include declarations.
44 */
45 #include "MagickCore/studio.h"
46 #include "MagickCore/annotate.h"
47 #include "MagickCore/annotate-private.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/cache-private.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/composite.h"
57 #include "MagickCore/composite-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/draw-private.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/gem.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/log.h"
68 #include "MagickCore/quantum.h"
69 #include "MagickCore/quantum-private.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/policy.h"
72 #include "MagickCore/property.h"
73 #include "MagickCore/resource_.h"
74 #include "MagickCore/semaphore.h"
75 #include "MagickCore/statistic.h"
76 #include "MagickCore/string_.h"
77 #include "MagickCore/token.h"
78 #include "MagickCore/token-private.h"
79 #include "MagickCore/transform.h"
80 #include "MagickCore/transform-private.h"
81 #include "MagickCore/type.h"
82 #include "MagickCore/utility.h"
83 #include "MagickCore/utility-private.h"
84 #include "MagickCore/xwindow.h"
85 #include "MagickCore/xwindow-private.h"
86 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
87 #if defined(__MINGW32__)
88 # undef interface
89 #endif
90 #include <ft2build.h>
91 #if defined(FT_FREETYPE_H)
92 # include FT_FREETYPE_H
93 #else
94 # include <freetype/freetype.h>
95 #endif
96 #if defined(FT_GLYPH_H)
97 # include FT_GLYPH_H
98 #else
99 # include <freetype/ftglyph.h>
100 #endif
101 #if defined(FT_OUTLINE_H)
102 # include FT_OUTLINE_H
103 #else
104 # include <freetype/ftoutln.h>
105 #endif
106 #if defined(FT_BBOX_H)
107 # include FT_BBOX_H
108 #else
109 # include <freetype/ftbbox.h>
110 #endif
111 #if defined(FT_MODULE_H)
112 # include FT_MODULE_H
113 #else
114 # include <freetype/ftmodapi.h>
115 #endif
116 #endif
117 #if defined(MAGICKCORE_RAQM_DELEGATE)
118 #include <raqm.h>
119 #endif
120 typedef struct _GraphemeInfo
121 {
122  ssize_t
123  index,
124  x_offset,
125  x_advance,
126  y_offset,
127  y_advance;
128 
129  size_t
130  cluster;
131 } GraphemeInfo;
132 ␌
133 /*
134  Annotate semaphores.
135 */
136 static SemaphoreInfo
137  *annotate_semaphore = (SemaphoreInfo *) NULL;
138 ␌
139 /*
140  Forward declarations.
141 */
142 static MagickBooleanType
143  RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
144  ExceptionInfo *),
145  RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
146  ExceptionInfo *),
147  RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
149  RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
150  ExceptionInfo *);
151 ␌
152 /*
153 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154 % %
155 % %
156 % %
157 + A n n o t a t e C o m p o n e n t G e n e s i s %
158 % %
159 % %
160 % %
161 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
162 %
163 % AnnotateComponentGenesis() instantiates the annotate component.
164 %
165 % The format of the AnnotateComponentGenesis method is:
166 %
167 % MagickBooleanType AnnotateComponentGenesis(void)
168 %
169 */
170 MagickPrivate MagickBooleanType AnnotateComponentGenesis(void)
171 {
172  if (annotate_semaphore == (SemaphoreInfo *) NULL)
173  annotate_semaphore=AcquireSemaphoreInfo();
174  return(MagickTrue);
175 }
176 ␌
177 /*
178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 % %
180 % %
181 % %
182 + 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 %
183 % %
184 % %
185 % %
186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 %
188 % AnnotateComponentTerminus() destroys the annotate component.
189 %
190 % The format of the AnnotateComponentTerminus method is:
191 %
192 % AnnotateComponentTerminus(void)
193 %
194 */
195 MagickPrivate void AnnotateComponentTerminus(void)
196 {
197  if (annotate_semaphore == (SemaphoreInfo *) NULL)
198  ActivateSemaphoreInfo(&annotate_semaphore);
199  RelinquishSemaphoreInfo(&annotate_semaphore);
200 }
201 ␌
202 /*
203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
204 % %
205 % %
206 % %
207 % A n n o t a t e I m a g e %
208 % %
209 % %
210 % %
211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212 %
213 % AnnotateImage() annotates an image with text.
214 %
215 % The format of the AnnotateImage method is:
216 %
217 % MagickBooleanType AnnotateImage(Image *image,DrawInfo *draw_info,
218 % ExceptionInfo *exception)
219 %
220 % A description of each parameter follows:
221 %
222 % o image: the image.
223 %
224 % o draw_info: the draw info.
225 %
226 % o exception: return any errors or warnings in this structure.
227 %
228 */
229 MagickExport MagickBooleanType AnnotateImage(Image *image,
230  const DrawInfo *draw_info,ExceptionInfo *exception)
231 {
232  char
233  *p,
234  color[MagickPathExtent],
235  primitive[MagickPathExtent],
236  *text,
237  **textlist;
238 
239  DrawInfo
240  *annotate,
241  *annotate_info;
242 
244  geometry_info;
245 
246  MagickBooleanType
247  status;
248 
249  PixelInfo
250  pixel;
251 
252  PointInfo
253  offset;
254 
256  geometry;
257 
258  size_t
259  height,
260  number_lines;
261 
262  ssize_t
263  i;
264 
265  TypeMetric
266  metrics;
267 
268  assert(image != (Image *) NULL);
269  assert(image->signature == MagickCoreSignature);
270  if (IsEventLogging() != MagickFalse)
271  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
272  assert(draw_info != (DrawInfo *) NULL);
273  assert(draw_info->signature == MagickCoreSignature);
274  if (draw_info->text == (char *) NULL)
275  return(MagickFalse);
276  if (*draw_info->text == '\0')
277  return(MagickTrue);
278  annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
279  text=annotate->text;
280  annotate->text=(char *) NULL;
281  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
282  number_lines=1;
283  for (p=text; *p != '\0'; p++)
284  if (*p == '\n')
285  number_lines++;
286  textlist=(char **) AcquireQuantumMemory(number_lines+1,sizeof(*textlist));
287  if (textlist == (char **) NULL)
288  {
289  annotate_info=DestroyDrawInfo(annotate_info);
290  annotate=DestroyDrawInfo(annotate);
291  text=DestroyString(text);
292  return(MagickFalse);
293  }
294  p=text;
295  for (i=0; i < (ssize_t) number_lines; i++)
296  {
297  char
298  *q;
299 
300  textlist[i]=p;
301  for (q=p; *q != '\0'; q++)
302  if ((*q == '\r') || (*q == '\n'))
303  break;
304  if (*q == '\r')
305  {
306  *q='\0';
307  q++;
308  }
309  *q='\0';
310  p=q+1;
311  }
312  textlist[i]=(char *) NULL;
313  SetGeometry(image,&geometry);
314  SetGeometryInfo(&geometry_info);
315  if (annotate_info->geometry != (char *) NULL)
316  {
317  (void) ParsePageGeometry(image,annotate_info->geometry,&geometry,
318  exception);
319  (void) ParseGeometry(annotate_info->geometry,&geometry_info);
320  }
321  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
322  {
323  annotate_info=DestroyDrawInfo(annotate_info);
324  annotate=DestroyDrawInfo(annotate);
325  textlist=(char **) RelinquishMagickMemory(textlist);
326  text=DestroyString(text);
327  return(MagickFalse);
328  }
329  if (IsGrayColorspace(image->colorspace) != MagickFalse)
330  (void) SetImageColorspace(image,sRGBColorspace,exception);
331  status=MagickTrue;
332  (void) memset(&metrics,0,sizeof(metrics));
333  for (i=0; textlist[i] != (char *) NULL; i++)
334  {
335  if (*textlist[i] == '\0')
336  continue;
337  /*
338  Position text relative to image.
339  */
340  annotate_info->affine.tx=geometry_info.xi-image->page.x;
341  annotate_info->affine.ty=geometry_info.psi-image->page.y;
342  (void) CloneString(&annotate->text,textlist[i]);
343  if ((metrics.width == 0) || (annotate->gravity != NorthWestGravity))
344  (void) GetTypeMetrics(image,annotate,&metrics,exception);
345  height=CastDoubleToSizeT(metrics.ascent-metrics.descent+0.5);
346  if (height == 0)
347  height=(size_t) draw_info->pointsize;
348  height=CastDoubleToSizeT(floor((double) height+
349  draw_info->interline_spacing+0.5));
350  switch (annotate->gravity)
351  {
352  case UndefinedGravity:
353  default:
354  {
355  offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
356  offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
357  break;
358  }
359  case (GravityType) NorthWestGravity:
360  {
361  offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
362  annotate_info->affine.ry*height+annotate_info->affine.ry*
363  (metrics.ascent+metrics.descent);
364  offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
365  (metrics.bounds.y2-metrics.ascent)+i*annotate_info->affine.sy*height+
366  annotate_info->affine.sy*metrics.ascent;
367  break;
368  }
369  case (GravityType) NorthGravity:
370  {
371  offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
372  geometry.width/2.0+i*annotate_info->affine.ry*height-
373  annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
374  (metrics.ascent+metrics.descent);
375  offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
376  (metrics.bounds.y2-metrics.ascent)+i*annotate_info->affine.sy*height+
377  annotate_info->affine.sy*metrics.ascent-annotate_info->affine.rx*
378  metrics.width/2.0;
379  break;
380  }
381  case (GravityType) NorthEastGravity:
382  {
383  offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
384  geometry.width+i*annotate_info->affine.ry*height-
385  annotate_info->affine.sx*metrics.width+annotate_info->affine.ry*
386  (metrics.ascent+metrics.descent)-1.0;
387  offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
388  (metrics.bounds.y2-metrics.ascent)+i*annotate_info->affine.sy*height+
389  annotate_info->affine.sy*metrics.ascent-annotate_info->affine.rx*
390  metrics.width;
391  break;
392  }
393  case (GravityType) WestGravity:
394  {
395  offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
396  annotate_info->affine.ry*height+annotate_info->affine.ry*
397  (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
398  offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
399  geometry.height/2.0+i*annotate_info->affine.sy*height+
400  annotate_info->affine.sy*(metrics.ascent+metrics.descent-
401  (number_lines-1.0)*height)/2.0;
402  break;
403  }
404  case (GravityType) CenterGravity:
405  {
406  offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
407  geometry.width/2.0+i*annotate_info->affine.ry*height-
408  annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
409  (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
410  offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
411  geometry.height/2.0+i*annotate_info->affine.sy*height-
412  annotate_info->affine.rx*metrics.width/2.0+annotate_info->affine.sy*
413  (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
414  break;
415  }
416  case (GravityType) EastGravity:
417  {
418  offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
419  geometry.width+i*annotate_info->affine.ry*height-
420  annotate_info->affine.sx*metrics.width+
421  annotate_info->affine.ry*(metrics.ascent+metrics.descent-
422  (number_lines-1.0)*height)/2.0-1.0;
423  offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
424  geometry.height/2.0+i*annotate_info->affine.sy*height-
425  annotate_info->affine.rx*metrics.width+
426  annotate_info->affine.sy*(metrics.ascent+metrics.descent-
427  (number_lines-1.0)*height)/2.0;
428  break;
429  }
430  case (GravityType) SouthWestGravity:
431  {
432  offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
433  annotate_info->affine.ry*height-annotate_info->affine.ry*
434  (number_lines-1.0)*height;
435  offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
436  geometry.height+i*annotate_info->affine.sy*height-
437  annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
438  break;
439  }
440  case (GravityType) SouthGravity:
441  {
442  offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
443  geometry.width/2.0+i*annotate_info->affine.ry*height-
444  annotate_info->affine.sx*metrics.width/2.0-
445  annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
446  offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
447  geometry.height+i*annotate_info->affine.sy*height-
448  annotate_info->affine.rx*metrics.width/2.0-
449  annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
450  break;
451  }
452  case (GravityType) SouthEastGravity:
453  {
454  offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
455  geometry.width+i*annotate_info->affine.ry*height-
456  annotate_info->affine.sx*metrics.width-
457  annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
458  offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
459  geometry.height+i*annotate_info->affine.sy*height-
460  annotate_info->affine.rx*metrics.width-
461  annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
462  break;
463  }
464  }
465  switch (annotate->align)
466  {
467  case LeftAlign:
468  {
469  offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
470  offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
471  break;
472  }
473  case CenterAlign:
474  {
475  offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
476  annotate_info->affine.sx*metrics.width/2.0;
477  offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
478  annotate_info->affine.rx*metrics.width/2.0;
479  break;
480  }
481  case RightAlign:
482  {
483  offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
484  annotate_info->affine.sx*metrics.width;
485  offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
486  annotate_info->affine.rx*metrics.width;
487  break;
488  }
489  default:
490  break;
491  }
492  if (draw_info->undercolor.alpha != (MagickRealType) TransparentAlpha)
493  {
494  DrawInfo
495  *undercolor_info;
496 
497  /*
498  Text box.
499  */
500  undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
501  undercolor_info->fill=draw_info->undercolor;
502  undercolor_info->affine=draw_info->affine;
503  undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
504  undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
505  (void) FormatLocaleString(primitive,MagickPathExtent,
506  "rectangle 0.0,0.0 %g,%g",metrics.origin.x,(double) height);
507  (void) CloneString(&undercolor_info->primitive,primitive);
508  (void) DrawImage(image,undercolor_info,exception);
509  (void) DestroyDrawInfo(undercolor_info);
510  }
511  annotate_info->affine.tx=offset.x;
512  annotate_info->affine.ty=offset.y;
513  pixel=annotate_info->fill;
514  if (annotate_info->stroke.alpha != (MagickRealType) TransparentAlpha)
515  pixel=annotate_info->stroke;
516  (void) QueryColorname(image,&pixel,AllCompliance,color,exception);
517  (void) FormatLocaleString(primitive,MagickPathExtent,"stroke %s "
518  "stroke-width %g line 0,0 %g,0",color,(double)
519  metrics.underline_thickness,(double) metrics.width);
520  /*
521  Annotate image with text.
522  */
523  switch (annotate->decorate)
524  {
525  case OverlineDecoration:
526  {
527  annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
528  metrics.descent-metrics.underline_position));
529  (void) CloneString(&annotate_info->primitive,primitive);
530  (void) DrawImage(image,annotate_info,exception);
531  break;
532  }
533  case UnderlineDecoration:
534  {
535  annotate_info->affine.ty-=(draw_info->affine.sy*
536  metrics.underline_position);
537  (void) CloneString(&annotate_info->primitive,primitive);
538  (void) DrawImage(image,annotate_info,exception);
539  break;
540  }
541  default:
542  break;
543  }
544  status=RenderType(image,annotate,&offset,&metrics,exception);
545  if (status == MagickFalse)
546  break;
547 
548  if (annotate->decorate == LineThroughDecoration) {
549  annotate_info->affine.ty-=(draw_info->affine.sy*(height+
550  metrics.underline_position+metrics.descent*2)/2.0);
551  (void) CloneString(&annotate_info->primitive,primitive);
552  (void) DrawImage(image,annotate_info,exception);
553  }
554  }
555  /*
556  Relinquish resources.
557  */
558  annotate_info=DestroyDrawInfo(annotate_info);
559  annotate=DestroyDrawInfo(annotate);
560  textlist=(char **) RelinquishMagickMemory(textlist);
561  text=DestroyString(text);
562  return(status);
563 }
564 ␌
565 /*
566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
567 % %
568 % %
569 % %
570 % F o r m a t M a g i c k C a p t i o n %
571 % %
572 % %
573 % %
574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575 %
576 % FormatMagickCaption() formats a caption so that it fits within the image
577 % width. It returns the number of lines in the formatted caption.
578 %
579 % The format of the FormatMagickCaption method is:
580 %
581 % ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
582 % const MagickBooleanType split,TypeMetric *metrics,char **caption,
583 % ExceptionInfo *exception)
584 %
585 % A description of each parameter follows.
586 %
587 % o image: The image.
588 %
589 % o draw_info: the draw info.
590 %
591 % o split: when no convenient line breaks-- insert newline.
592 %
593 % o metrics: Return the font metrics in this structure.
594 %
595 % o caption: the caption.
596 %
597 % o exception: return any errors or warnings in this structure.
598 %
599 */
600 
601 static inline char *ReplaceSpaceWithNewline(char **caption,char *space)
602 {
603  size_t
604  octets;
605 
606  if ((caption == (char **) NULL) || (space == (char *) NULL))
607  return((char *) NULL);
608  octets=(size_t) GetUTFOctets(space);
609  if (octets == 1)
610  *space='\n';
611  else
612  {
613  char
614  *target;
615 
616  size_t
617  length;
618 
619  ssize_t
620  offset;
621 
622  length=strlen(*caption);
623  *space='\n';
624  offset=space-(*caption);
625  if (offset >= 0)
626  {
627  target=AcquireString(*caption);
628  (void) CopyMagickString(target,*caption,(size_t) offset+2);
629  (void) ConcatenateMagickString(target,space+octets,length);
630  (void) DestroyString(*caption);
631  *caption=target;
632  space=(*caption)+offset;
633  }
634  }
635  return(space);
636 }
637 
638 MagickExport ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
639  const MagickBooleanType split,TypeMetric *metrics,char **caption,
640  ExceptionInfo *exception)
641 {
642  char
643  *p,
644  *q,
645  *s;
646 
647  MagickBooleanType
648  status;
649 
650  size_t
651  width;
652 
653  ssize_t
654  i,
655  n;
656 
657  q=draw_info->text;
658  s=(char *) NULL;
659  width=0;
660  for (p=(*caption); GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p))
661  {
662  int
663  code;
664 
665  code=GetUTFCode(p);
666  if (code == '\n')
667  {
668  q=draw_info->text;
669  continue;
670  }
671  if ((draw_info->word_break != BreakWordBreakType) &&
672  (IsUTFSpace(code) != MagickFalse) &&
673  (IsNonBreakingUTFSpace(code) == MagickFalse))
674  {
675  if (width > image->columns)
676  p=ReplaceSpaceWithNewline(caption,p);
677  s=p;
678  }
679  for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
680  *q++=(*(p+i));
681  *q='\0';
682  status=GetTypeMetrics(image,draw_info,metrics,exception);
683  if (status == MagickFalse)
684  break;
685  width=CastDoubleToSizeT(metrics->width+draw_info->stroke_width+0.5);
686  if (width <= image->columns)
687  continue;
688  if (s != (char *) NULL)
689  p=ReplaceSpaceWithNewline(caption,s);
690  else
691  if ((split != MagickFalse) || (GetUTFOctets(p) > 2))
692  {
693  /*
694  No convenient line breaks-- insert newline.
695  */
696  n=p-(*caption);
697  if ((n > 0) && ((*caption)[n-1] != '\n'))
698  {
699  char
700  *target;
701 
702  target=AcquireString(*caption);
703  (void) CopyMagickString(target,*caption,(size_t) n+1);
704  (void) ConcatenateMagickString(target,"\n",strlen(*caption)+1);
705  (void) ConcatenateMagickString(target,p,strlen(*caption)+2);
706  (void) DestroyString(*caption);
707  *caption=target;
708  p=(*caption)+n;
709  }
710  }
711  q=draw_info->text;
712  s=(char *) NULL;
713  }
714  n=0;
715  for (p=(*caption); GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p))
716  if (GetUTFCode(p) == '\n')
717  n++;
718  return(n);
719 }
720 ␌
721 /*
722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
723 % %
724 % %
725 % %
726 % G e t M u l t i l i n e T y p e M e t r i c s %
727 % %
728 % %
729 % %
730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
731 %
732 % GetMultilineTypeMetrics() returns the following information for the
733 % specified font and text:
734 %
735 % character width
736 % character height
737 % ascender
738 % descender
739 % text width
740 % text height
741 % maximum horizontal advance
742 % bounds: x1
743 % bounds: y1
744 % bounds: x2
745 % bounds: y2
746 % origin: x
747 % origin: y
748 % underline position
749 % underline thickness
750 %
751 % This method is like GetTypeMetrics() but it returns the maximum text width
752 % and height for multiple lines of text.
753 %
754 % The format of the GetMultilineTypeMetrics method is:
755 %
756 % MagickBooleanType GetMultilineTypeMetrics(Image *image,
757 % const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
758 %
759 % A description of each parameter follows:
760 %
761 % o image: the image.
762 %
763 % o draw_info: the draw info.
764 %
765 % o metrics: Return the font metrics in this structure.
766 %
767 % o exception: return any errors or warnings in this structure.
768 %
769 */
770 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
771  const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
772 {
773  char
774  **textlist;
775 
776  double
777  height;
778 
779  DrawInfo
780  *annotate_info;
781 
782  MagickBooleanType
783  status;
784 
785  MagickSizeType
786  size;
787 
788  ssize_t
789  i;
790 
791  size_t
792  count;
793 
794  TypeMetric
795  extent;
796 
797  assert(image != (Image *) NULL);
798  assert(image->signature == MagickCoreSignature);
799  if (IsEventLogging() != MagickFalse)
800  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
801  assert(draw_info != (DrawInfo *) NULL);
802  assert(draw_info->text != (char *) NULL);
803  assert(draw_info->signature == MagickCoreSignature);
804  if (*draw_info->text == '\0')
805  {
806  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
807  "LabelExpected","`%s'",image->filename);
808  return(MagickFalse);
809  }
810  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
811  annotate_info->text=DestroyString(annotate_info->text);
812  /*
813  Convert newlines to multiple lines of text.
814  */
815  textlist=StringToStrings(draw_info->text,&count);
816  if (textlist == (char **) NULL)
817  {
818  annotate_info=DestroyDrawInfo(annotate_info);
819  return(MagickFalse);
820  }
821  annotate_info->render=MagickFalse;
822  annotate_info->direction=UndefinedDirection;
823  (void) memset(metrics,0,sizeof(*metrics));
824  (void) memset(&extent,0,sizeof(extent));
825  /*
826  Find the widest of the text lines.
827  */
828  annotate_info->text=textlist[0];
829  status=GetTypeMetrics(image,annotate_info,&extent,exception);
830  *metrics=extent;
831  height=(count*(size_t) (metrics->ascent-metrics->descent+
832  0.5)+(count-1)*draw_info->interline_spacing);
833  size=(MagickSizeType) fabs(height);
834  if (AcquireMagickResource(HeightResource,size) == MagickFalse)
835  {
836  (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
837  "WidthOrHeightExceedsLimit","`%s'",image->filename);
838  status=MagickFalse;
839  }
840  else
841  {
842  for (i=1; i < (ssize_t) count; i++)
843  {
844  annotate_info->text=textlist[i];
845  status=GetTypeMetrics(image,annotate_info,&extent,exception);
846  if (status == MagickFalse)
847  break;
848  if (extent.width > metrics->width)
849  *metrics=extent;
850  size=(MagickSizeType) fabs(extent.width);
851  if (AcquireMagickResource(WidthResource,size) == MagickFalse)
852  {
853  (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
854  "WidthOrHeightExceedsLimit","`%s'",image->filename);
855  status=MagickFalse;
856  break;
857  }
858  }
859  metrics->height=(double) height;
860  }
861  /*
862  Relinquish resources.
863  */
864  annotate_info->text=(char *) NULL;
865  annotate_info=DestroyDrawInfo(annotate_info);
866  for (i=0; i < (ssize_t) count; i++)
867  textlist[i]=DestroyString(textlist[i]);
868  textlist=(char **) RelinquishMagickMemory(textlist);
869  return(status);
870 }
871 ␌
872 /*
873 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
874 % %
875 % %
876 % %
877 % G e t T y p e M e t r i c s %
878 % %
879 % %
880 % %
881 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
882 %
883 % GetTypeMetrics() returns the following information for the specified font
884 % and text:
885 %
886 % character width
887 % character height
888 % ascender
889 % descender
890 % text width
891 % text height
892 % maximum horizontal advance
893 % bounds: x1
894 % bounds: y1
895 % bounds: x2
896 % bounds: y2
897 % origin: x
898 % origin: y
899 % underline position
900 % underline thickness
901 %
902 % The format of the GetTypeMetrics method is:
903 %
904 % MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
905 % TypeMetric *metrics,ExceptionInfo *exception)
906 %
907 % A description of each parameter follows:
908 %
909 % o image: the image.
910 %
911 % o draw_info: the draw info.
912 %
913 % o metrics: Return the font metrics in this structure.
914 %
915 % o exception: return any errors or warnings in this structure.
916 %
917 */
918 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
919  const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
920 {
921  DrawInfo
922  *annotate_info;
923 
924  MagickBooleanType
925  status;
926 
927  PointInfo
928  offset;
929 
930  assert(image != (Image *) NULL);
931  assert(image->signature == MagickCoreSignature);
932  if (IsEventLogging() != MagickFalse)
933  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
934  assert(draw_info != (DrawInfo *) NULL);
935  assert(draw_info->text != (char *) NULL);
936  assert(draw_info->signature == MagickCoreSignature);
937  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
938  annotate_info->render=MagickFalse;
939  annotate_info->direction=UndefinedDirection;
940  (void) memset(metrics,0,sizeof(*metrics));
941  offset.x=0.0;
942  offset.y=0.0;
943  status=RenderType(image,annotate_info,&offset,metrics,exception);
944  if (draw_info->debug != MagickFalse)
945  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
946  "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
947  "bounds: %g,%g %g,%g; origin: %g,%g; pixels per em: %g,%g; "
948  "underline position: %g; underline thickness: %g",annotate_info->text,
949  metrics->width,metrics->height,metrics->ascent,metrics->descent,
950  metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
951  metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
952  metrics->pixels_per_em.x,metrics->pixels_per_em.y,
953  metrics->underline_position,metrics->underline_thickness);
954  annotate_info=DestroyDrawInfo(annotate_info);
955  return(status);
956 }
957 ␌
958 /*
959 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960 % %
961 % %
962 % %
963 + R e n d e r T y p e %
964 % %
965 % %
966 % %
967 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
968 %
969 % RenderType() renders text on the image. It also returns the bounding box of
970 % the text relative to the image.
971 %
972 % The format of the RenderType method is:
973 %
974 % MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
975 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
976 %
977 % A description of each parameter follows:
978 %
979 % o image: the image.
980 %
981 % o draw_info: the draw info.
982 %
983 % o offset: (x,y) location of text relative to image.
984 %
985 % o metrics: bounding box of text.
986 %
987 % o exception: return any errors or warnings in this structure.
988 %
989 */
990 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
991  const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
992 {
993  char
994  *font;
995 
996  const TypeInfo
997  *type_info;
998 
999  DrawInfo
1000  *annotate_info;
1001 
1002  MagickBooleanType
1003  status;
1004 
1005  type_info=(const TypeInfo *) NULL;
1006  if (draw_info->font != (char *) NULL)
1007  {
1008  if (*draw_info->font == '@')
1009  {
1010  status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
1011  metrics,exception);
1012  return(status);
1013  }
1014  if (*draw_info->font == '-')
1015  return(RenderX11(image,draw_info,offset,metrics,exception));
1016  if (*draw_info->font == '^')
1017  return(RenderPostscript(image,draw_info,offset,metrics,exception));
1018  if (IsPathAccessible(draw_info->font) != MagickFalse)
1019  {
1020  status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
1021  metrics,exception);
1022  return(status);
1023  }
1024  type_info=GetTypeInfo(draw_info->font,exception);
1025  if (type_info == (const TypeInfo *) NULL)
1026  (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1027  "UnableToReadFont","`%s'",draw_info->font);
1028  }
1029  if ((type_info == (const TypeInfo *) NULL) &&
1030  (draw_info->family != (const char *) NULL))
1031  {
1032  if (strpbrk(draw_info->family,",'\"") == (char *) NULL)
1033  type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
1034  draw_info->stretch,draw_info->weight,exception);
1035  if (type_info == (const TypeInfo *) NULL)
1036  {
1037  char
1038  **family;
1039 
1040  int
1041  number_families;
1042 
1043  ssize_t
1044  i;
1045 
1046  /*
1047  Parse font family list.
1048  */
1049  family=StringToArgv(draw_info->family,&number_families);
1050  for (i=1; i < (ssize_t) number_families; i++)
1051  {
1052  (void) SubstituteString(&family[i],",","");
1053  type_info=GetTypeInfoByFamily(family[i],draw_info->style,
1054  draw_info->stretch,draw_info->weight,exception);
1055  if ((type_info != (const TypeInfo *) NULL) &&
1056  (LocaleCompare(family[i],type_info->family) == 0))
1057  break;
1058  }
1059  for (i=0; i < (ssize_t) number_families; i++)
1060  family[i]=DestroyString(family[i]);
1061  family=(char **) RelinquishMagickMemory(family);
1062  if (type_info == (const TypeInfo *) NULL)
1063  (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1064  "UnableToReadFont","`%s'",draw_info->family);
1065  }
1066  }
1067  font=GetPolicyValue("system:font");
1068  if (font != (char *) NULL)
1069  {
1070  if (IsPathAccessible(font) != MagickFalse)
1071  {
1072  /*
1073  Render with default system font.
1074  */
1075  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1076  annotate_info->font=font;
1077  status=RenderFreetype(image,annotate_info,annotate_info->encoding,
1078  offset,metrics,exception);
1079  annotate_info=DestroyDrawInfo(annotate_info);
1080  return(status);
1081  }
1082  font=DestroyString(font);
1083  }
1084  if (type_info == (const TypeInfo *) NULL)
1085  {
1087  *sans_exception;
1088 
1089  /*
1090  Search for a default font.
1091  */
1092  sans_exception=AcquireExceptionInfo();
1093  if (type_info == (const TypeInfo *) NULL)
1094  type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
1095  draw_info->stretch,draw_info->weight,sans_exception);
1096  if (type_info == (const TypeInfo *) NULL)
1097  type_info=GetTypeInfo("*",sans_exception);
1098  sans_exception=DestroyExceptionInfo(sans_exception);
1099  }
1100  if (type_info == (const TypeInfo *) NULL)
1101  {
1102  status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics,
1103  exception);
1104  return(status);
1105  }
1106  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1107  annotate_info->face=type_info->face;
1108  if (type_info->metrics != (char *) NULL)
1109  (void) CloneString(&annotate_info->metrics,type_info->metrics);
1110  if (type_info->glyphs != (char *) NULL)
1111  (void) CloneString(&annotate_info->font,type_info->glyphs);
1112  status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics,
1113  exception);
1114  annotate_info=DestroyDrawInfo(annotate_info);
1115  return(status);
1116 }
1117 ␌
1118 /*
1119 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120 % %
1121 % %
1122 % %
1123 + R e n d e r F r e e t y p e %
1124 % %
1125 % %
1126 % %
1127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128 %
1129 % RenderFreetype() renders text on the image with a Truetype font. It also
1130 % returns the bounding box of the text relative to the image.
1131 %
1132 % The format of the RenderFreetype method is:
1133 %
1134 % MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
1135 % const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1136 % ExceptionInfo *exception)
1137 %
1138 % A description of each parameter follows:
1139 %
1140 % o image: the image.
1141 %
1142 % o draw_info: the draw info.
1143 %
1144 % o encoding: the font encoding.
1145 %
1146 % o offset: (x,y) location of text relative to image.
1147 %
1148 % o metrics: bounding box of text.
1149 %
1150 % o exception: return any errors or warnings in this structure.
1151 %
1152 */
1153 
1154 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
1155 
1156 #if defined(MAGICKCORE_RAQM_DELEGATE)
1157 static MagickBooleanType ContainsRaqmParagraphSeparator(const unsigned char *p,
1158  const size_t length)
1159 {
1160  size_t
1161  i;
1162 
1163  if ((p == (const unsigned char*) NULL) || (length == 0))
1164  return(MagickFalse);
1165 
1166  for (i = 0; i < length; i++)
1167  {
1168  /* LF or CR */
1169  if ((p[i] == '\n') || (p[i] == '\r'))
1170  return(MagickTrue);
1171 
1172  /* UTF-8 NEL U+0085 => C2 85 */
1173  if ((p[i] == 0xC2) && (i + 1 < length) && (p[i + 1] == 0x85))
1174  return(MagickTrue);
1175 
1176  /* UTF-8 LS/PS U+2028/U+2029 => E2 80 A8/A9 */
1177  if ((p[i] == 0xE2) && (i + 2 < length) && (p[i + 1] == 0x80) &&
1178  ((p[i + 2] == 0xA8) || (p[i + 2] == 0xA9)))
1179  return(MagickTrue);
1180  }
1181 
1182  return(MagickFalse);
1183 }
1184 
1185 static size_t ComplexRaqmTextLayout(const Image *image,
1186  const DrawInfo *draw_info,const char *text,const size_t length,
1187  const FT_Face face,GraphemeInfo **grapheme,ExceptionInfo *exception)
1188 {
1189  const char
1190  *features;
1191 
1192  raqm_t
1193  *rq;
1194 
1195  raqm_glyph_t
1196  *glyphs;
1197 
1198  size_t
1199  extent;
1200 
1201  ssize_t
1202  i;
1203 
1204  extent=0;
1205  rq=raqm_create();
1206  if (rq == (raqm_t *) NULL)
1207  goto cleanup;
1208  if (raqm_set_text_utf8(rq,text,length) == 0)
1209  goto cleanup;
1210  if (raqm_set_par_direction(rq,(raqm_direction_t) draw_info->direction) == 0)
1211  goto cleanup;
1212  if (raqm_set_freetype_face(rq,face) == 0)
1213  goto cleanup;
1214  features=GetImageProperty(image,"type:features",exception);
1215  if (features != (const char *) NULL)
1216  {
1217  char
1218  breaker,
1219  quote,
1220  *token;
1221 
1222  int
1223  next,
1224  status_token;
1225 
1226  TokenInfo
1227  *token_info;
1228 
1229  next=0;
1230  token_info=AcquireTokenInfo();
1231  token=AcquireString("");
1232  status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1233  &breaker,&next,&quote);
1234  while (status_token == 0)
1235  {
1236  raqm_add_font_feature(rq,token,(int) strlen(token));
1237  status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1238  &breaker,&next,&quote);
1239  }
1240  token_info=DestroyTokenInfo(token_info);
1241  token=DestroyString(token);
1242  }
1243  if (raqm_layout(rq) == 0)
1244  goto cleanup;
1245  glyphs=raqm_get_glyphs(rq,&extent);
1246  if (glyphs == (raqm_glyph_t *) NULL)
1247  {
1248  extent=0;
1249  goto cleanup;
1250  }
1251  *grapheme=(GraphemeInfo *) AcquireQuantumMemory(extent,sizeof(**grapheme));
1252  if (*grapheme == (GraphemeInfo *) NULL)
1253  {
1254  extent=0;
1255  goto cleanup;
1256  }
1257  for (i=0; i < (ssize_t) extent; i++)
1258  {
1259  (*grapheme)[i].index=glyphs[i].index;
1260  (*grapheme)[i].x_offset=glyphs[i].x_offset;
1261  (*grapheme)[i].x_advance=glyphs[i].x_advance;
1262  (*grapheme)[i].y_offset=glyphs[i].y_offset;
1263  (*grapheme)[i].y_advance=glyphs[i].y_advance;
1264  (*grapheme)[i].cluster=glyphs[i].cluster;
1265  }
1266 
1267 cleanup:
1268  raqm_destroy(rq);
1269  return(extent);
1270 }
1271 #endif
1272 
1273 static size_t ComplexTextLayout(const DrawInfo *draw_info,const char *text,
1274  const size_t length,const FT_Face face,const FT_Int32 flags,
1275  GraphemeInfo **grapheme)
1276 {
1277  const char
1278  *p;
1279 
1280  ssize_t
1281  i;
1282 
1283  ssize_t
1284  last_glyph;
1285 
1286  /*
1287  Simple layout for bi-directional text (right-to-left or left-to-right).
1288  */
1289  *grapheme=(GraphemeInfo *) AcquireQuantumMemory(length+1,sizeof(**grapheme));
1290  if (*grapheme == (GraphemeInfo *) NULL)
1291  return(0);
1292  last_glyph=0;
1293  p=text;
1294  for (i=0; GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p), i++)
1295  {
1296  (*grapheme)[i].index=(ssize_t) FT_Get_Char_Index(face,(FT_ULong)
1297  GetUTFCode(p));
1298  (*grapheme)[i].x_offset=0;
1299  (*grapheme)[i].y_offset=0;
1300  if (((*grapheme)[i].index != 0) && (last_glyph != 0))
1301  {
1302  if (FT_HAS_KERNING(face))
1303  {
1304  FT_Error
1305  ft_status;
1306 
1307  FT_Vector
1308  kerning;
1309 
1310  ft_status=FT_Get_Kerning(face,(FT_UInt) last_glyph,(FT_UInt)
1311  (*grapheme)[i].index,ft_kerning_default,&kerning);
1312  if (ft_status == 0)
1313  (*grapheme)[i-1].x_advance+=(FT_Pos) ((draw_info->direction ==
1314  RightToLeftDirection ? -1.0 : 1.0)*kerning.x);
1315  }
1316  }
1317  (void) FT_Load_Glyph(face,(FT_UInt) (*grapheme)[i].index,flags);
1318  (*grapheme)[i].x_advance=face->glyph->advance.x;
1319  (*grapheme)[i].y_advance=face->glyph->advance.y;
1320  (*grapheme)[i].cluster=(size_t) (p-text);
1321  last_glyph=(*grapheme)[i].index;
1322  }
1323  return((size_t) i);
1324 }
1325 
1326 static void FreetypeCloseStream(FT_Stream stream)
1327 {
1328  FILE
1329  *file;
1330 
1331  file=(FILE *) stream->descriptor.pointer;
1332  if (file != (FILE *) NULL)
1333  (void) fclose(file);
1334  stream->descriptor.pointer=NULL;
1335 }
1336 
1337 static unsigned long FreetypeReadStream(FT_Stream stream,unsigned long offset,
1338  unsigned char *buffer,unsigned long count)
1339 {
1340  FILE
1341  *file;
1342 
1343  unsigned long
1344  result;
1345 
1346  file=(FILE *) stream->descriptor.pointer;
1347  if (file == (FILE *) NULL)
1348  return(0);
1349  if (offset > stream->size)
1350  result=1;
1351  else
1352  result=(unsigned long) fseek(file,(off_t) offset,SEEK_SET);
1353  if (count == 0) /* seek operation */
1354  return(result);
1355  if (result != 0)
1356  return(0);
1357  return((unsigned long) fread(buffer,1,count,file));
1358 }
1359 
1360 static inline MagickBooleanType IsEmptyOutline(FT_Outline outline)
1361 {
1362  return((outline.n_points == 0) || (outline.n_contours <= 0) ? MagickTrue :
1363  MagickFalse);
1364 }
1365 
1366 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
1367  DrawInfo *draw_info)
1368 {
1369  AffineMatrix
1370  affine;
1371 
1372  char
1373  path[MagickPathExtent];
1374 
1375  affine=draw_info->affine;
1376  (void) FormatLocaleString(path,MagickPathExtent,"C%g,%g %g,%g %g,%g",
1377  affine.tx+p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,affine.ty-
1378  q->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1379  (void) ConcatenateString(&draw_info->primitive,path);
1380  return(0);
1381 }
1382 
1383 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
1384 {
1385  AffineMatrix
1386  affine;
1387 
1388  char
1389  path[MagickPathExtent];
1390 
1391  affine=draw_info->affine;
1392  (void) FormatLocaleString(path,MagickPathExtent,"L%g,%g",affine.tx+to->x/64.0,
1393  affine.ty-to->y/64.0);
1394  (void) ConcatenateString(&draw_info->primitive,path);
1395  return(0);
1396 }
1397 
1398 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
1399 {
1400  AffineMatrix
1401  affine;
1402 
1403  char
1404  path[MagickPathExtent];
1405 
1406  affine=draw_info->affine;
1407  (void) FormatLocaleString(path,MagickPathExtent,"M%g,%g",affine.tx+to->x/64.0,
1408  affine.ty-to->y/64.0);
1409  (void) ConcatenateString(&draw_info->primitive,path);
1410  return(0);
1411 }
1412 
1413 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
1414  DrawInfo *draw_info)
1415 {
1416  AffineMatrix
1417  affine;
1418 
1419  char
1420  path[MagickPathExtent];
1421 
1422  affine=draw_info->affine;
1423  (void) FormatLocaleString(path,MagickPathExtent,"Q%g,%g %g,%g",affine.tx+
1424  control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,affine.ty-
1425  to->y/64.0);
1426  (void) ConcatenateString(&draw_info->primitive,path);
1427  return(0);
1428 }
1429 
1430 #if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 10
1431 static inline const char *FreetypeErrorMessage(FT_Error ft_status)
1432 {
1433  return(FT_Error_String(ft_status));
1434 #else
1435 static inline const char *FreetypeErrorMessage(
1436  FT_Error magick_unused(ft_status))
1437 {
1438  magick_unreferenced(ft_status);
1439  return((const char *) NULL);
1440 #endif
1441 }
1442 
1443 static void *FreetypeAlloc(FT_Memory magick_unused(memory),long size)
1444 {
1445  magick_unreferenced(memory);
1446  if (size < 0)
1447  return((void *) NULL);
1448  if ((size_t) size > GetMaxMemoryRequest())
1449  return((void *) NULL);
1450  return(AcquireMagickMemory((size_t) size));
1451 }
1452 
1453 static void *FreetypeRealloc(FT_Memory magick_unused(memory),
1454  long magick_unused(cur_size),long size,void *block)
1455 {
1456  magick_unreferenced(memory);
1457  magick_unreferenced(cur_size);
1458  if (size < 0)
1459  return((void *) NULL);
1460  if ((size_t) size > GetMaxMemoryRequest())
1461  return((void *) NULL);
1462  return(ResizeMagickMemory(block,(size_t) size));
1463 }
1464 
1465 static void FreetypeFree(FT_Memory magick_unused(memory),void *block)
1466 {
1467  magick_unreferenced(memory);
1468  (void) RelinquishMagickMemory(block);
1469 }
1470 
1471 static FT_Memory FreetypeAcquireMemoryManager(void)
1472 {
1473  FT_Memory
1474  memory;
1475 
1476  memory=(FT_Memory) AcquireMagickMemory(sizeof(*memory));
1477  if (memory == (FT_Memory) NULL)
1478  return(memory);
1479  memset(memory,0,sizeof(*memory));
1480  memory->alloc=(&FreetypeAlloc);
1481  memory->realloc=(&FreetypeRealloc);
1482  memory->free=(&FreetypeFree);
1483  return(memory);
1484 }
1485 
1486 static void FreetypeDone(FT_Memory memory,FT_Library library,
1487  FT_StreamRec *stream)
1488 {
1489  (void) FT_Done_Library(library);
1490  stream=(FT_StreamRec *) RelinquishMagickMemory(stream);
1491  memory=(FT_Memory) RelinquishMagickMemory(memory);
1492 }
1493 
1494 static FT_Error FreetypeInit(FT_Memory memory,FT_Library *alibrary)
1495 {
1496  FT_Error
1497  ft_status;
1498 
1499  ft_status=FT_New_Library(memory,alibrary);
1500  if (ft_status != 0)
1501  RelinquishMagickMemory(memory);
1502  else
1503  FT_Add_Default_Modules(*alibrary);
1504  FT_Set_Default_Properties(*alibrary);
1505  return(ft_status);
1506 }
1507 
1508 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1509  const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1510  ExceptionInfo *exception)
1511 {
1512 #if !defined(FT_OPEN_PATHNAME)
1513 #define FT_OPEN_PATHNAME ft_open_pathname
1514 #endif
1515 
1516 #define ThrowFreetypeErrorException(tag,ft_status,value) \
1517 { \
1518  const char *error_string = FreetypeErrorMessage(ft_status); \
1519  if (error_string != (const char *) NULL) \
1520  (void) ThrowMagickException(exception,GetMagickModule(),TypeError, \
1521  tag,"`%s (%s)'",value, error_string); \
1522  else \
1523  (void) ThrowMagickException(exception,GetMagickModule(),TypeError, \
1524  tag,"`%s'",value); \
1525 }
1526 
1527  typedef struct _GlyphInfo
1528  {
1529  FT_UInt
1530  id;
1531 
1532  FT_Vector
1533  origin;
1534 
1535  FT_Glyph
1536  image;
1537  } GlyphInfo;
1538 
1539  char
1540  *p;
1541 
1542  const char
1543  *value;
1544 
1545  DrawInfo
1546  *annotate_info;
1547 
1548  FT_BBox
1549  bounds;
1550 
1551  FT_BitmapGlyph
1552  bitmap;
1553 
1554  FT_Encoding
1555  encoding_type;
1556 
1557  FT_Error
1558  ft_status;
1559 
1560  FT_Face
1561  face;
1562 
1563  FT_Int32
1564  flags;
1565 
1566  FT_Library
1567  library;
1568 
1569  FT_Long
1570  face_index;
1571 
1572  FT_Matrix
1573  affine;
1574 
1575  FT_Memory
1576  memory;
1577 
1578  FT_Open_Args
1579  args;
1580 
1581  FT_StreamRec
1582  *stream;
1583 
1584  FT_UInt
1585  first_glyph_id,
1586  last_glyph_id,
1587  missing_glyph_id;
1588 
1589  FT_Vector
1590  origin;
1591 
1592  GlyphInfo
1593  glyph;
1594 
1595  GraphemeInfo
1596  *grapheme;
1597 
1598  MagickBooleanType
1599  status;
1600 
1601  PointInfo
1602  point,
1603  resolution;
1604 
1605  ssize_t
1606  i;
1607 
1608  size_t
1609  length;
1610 
1611  ssize_t
1612  code,
1613  last_character,
1614  y;
1615 
1616  static FT_Outline_Funcs
1617  OutlineMethods =
1618  {
1619  (FT_Outline_MoveTo_Func) TraceMoveTo,
1620  (FT_Outline_LineTo_Func) TraceLineTo,
1621  (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1622  (FT_Outline_CubicTo_Func) TraceCubicBezier,
1623  0, 0
1624  };
1625 
1626  struct stat
1627  attributes;
1628 
1629  unsigned char
1630  *utf8;
1631 
1632  /*
1633  Initialize Truetype library.
1634  */
1635  if ((draw_info->font != (char *) NULL) && (*draw_info->font == '@') &&
1636  (IsRightsAuthorized(PathPolicyDomain,ReadPolicyRights,draw_info->font) == MagickFalse))
1637  ThrowPolicyException(draw_info->font,MagickFalse);
1638  memory=FreetypeAcquireMemoryManager();
1639  if (memory == (FT_Memory) NULL)
1640  ThrowBinaryException(ResourceLimitError,"UnableToInitializeFreetypeLibrary",
1641  image->filename);
1642  ft_status=FreetypeInit(memory,&library);
1643  if (ft_status != 0)
1644  {
1645  ThrowFreetypeErrorException("UnableToInitializeFreetypeLibrary",ft_status,
1646  image->filename);
1647  return(MagickFalse);
1648  }
1649  /*
1650  Open font face.
1651  */
1652  face_index=(FT_Long) draw_info->face;
1653  (void) memset(&args,0,sizeof(args));
1654  if (draw_info->font == (char *) NULL)
1655  {
1656  const TypeInfo *type_info = GetTypeInfo("*",exception);
1657  if (type_info != (const TypeInfo *) NULL)
1658  args.pathname=ConstantString(type_info->glyphs);
1659  }
1660  else
1661  if (*draw_info->font != '@')
1662  args.pathname=ConstantString(draw_info->font);
1663  else
1664  {
1665  /*
1666  Extract face index, e.g. @msgothic[1].
1667  */
1668  ImageInfo *image_info = AcquireImageInfo();
1669  (void) CopyMagickString(image_info->filename,draw_info->font+1,
1670  MagickPathExtent);
1671  (void) SetImageInfo(image_info,0,exception);
1672  face_index=(FT_Long) image_info->scene;
1673  args.pathname=ConstantString(image_info->filename);
1674  image_info=DestroyImageInfo(image_info);
1675  }
1676  /*
1677  Configure streaming interface.
1678  */
1679  stream=(FT_StreamRec *) AcquireCriticalMemory(sizeof(*stream));
1680  (void) memset(stream,0,sizeof(*stream));
1681  if (stat(args.pathname,&attributes) == 0)
1682  stream->size=attributes.st_size >= 0 ? (unsigned long)
1683  attributes.st_size : 0;
1684  stream->descriptor.pointer=fopen_utf8(args.pathname,"rb");
1685  stream->read=(&FreetypeReadStream);
1686  stream->close=(&FreetypeCloseStream);
1687  args.flags=FT_OPEN_STREAM;
1688  args.stream=stream;
1689  face=(FT_Face) NULL;
1690  ft_status=FT_Open_Face(library,&args,face_index,&face);
1691  if (ft_status != 0)
1692  {
1693  FreetypeDone(memory,library,stream);
1694  ThrowFreetypeErrorException("UnableToReadFont",ft_status,args.pathname);
1695  args.pathname=DestroyString(args.pathname);
1696  return(MagickFalse);
1697  }
1698  args.pathname=DestroyString(args.pathname);
1699  if ((draw_info->metrics != (char *) NULL) &&
1700  (IsPathAccessible(draw_info->metrics) != MagickFalse))
1701  (void) FT_Attach_File(face,draw_info->metrics);
1702  encoding_type=FT_ENCODING_UNICODE;
1703  ft_status=FT_Select_Charmap(face,encoding_type);
1704  if ((ft_status != 0) && (face->num_charmaps != 0))
1705  ft_status=FT_Set_Charmap(face,face->charmaps[0]);
1706  if (encoding != (const char *) NULL)
1707  {
1708  if (LocaleCompare(encoding,"AdobeCustom") == 0)
1709  encoding_type=FT_ENCODING_ADOBE_CUSTOM;
1710  if (LocaleCompare(encoding,"AdobeExpert") == 0)
1711  encoding_type=FT_ENCODING_ADOBE_EXPERT;
1712  if (LocaleCompare(encoding,"AdobeStandard") == 0)
1713  encoding_type=FT_ENCODING_ADOBE_STANDARD;
1714  if (LocaleCompare(encoding,"AppleRoman") == 0)
1715  encoding_type=FT_ENCODING_APPLE_ROMAN;
1716  if (LocaleCompare(encoding,"BIG5") == 0)
1717  encoding_type=FT_ENCODING_BIG5;
1718 #if defined(FT_ENCODING_PRC)
1719  if (LocaleCompare(encoding,"GB2312") == 0)
1720  encoding_type=FT_ENCODING_PRC;
1721 #endif
1722 #if defined(FT_ENCODING_JOHAB)
1723  if (LocaleCompare(encoding,"Johab") == 0)
1724  encoding_type=FT_ENCODING_JOHAB;
1725 #endif
1726 #if defined(FT_ENCODING_ADOBE_LATIN_1)
1727  if (LocaleCompare(encoding,"Latin-1") == 0)
1728  encoding_type=FT_ENCODING_ADOBE_LATIN_1;
1729 #endif
1730 #if defined(FT_ENCODING_ADOBE_LATIN_2)
1731  if (LocaleCompare(encoding,"Latin-2") == 0)
1732  encoding_type=FT_ENCODING_OLD_LATIN_2;
1733 #endif
1734  if (LocaleCompare(encoding,"None") == 0)
1735  encoding_type=FT_ENCODING_NONE;
1736  if (LocaleCompare(encoding,"SJIScode") == 0)
1737  encoding_type=FT_ENCODING_SJIS;
1738  if (LocaleCompare(encoding,"Symbol") == 0)
1739  encoding_type=FT_ENCODING_MS_SYMBOL;
1740  if (LocaleCompare(encoding,"Unicode") == 0)
1741  encoding_type=FT_ENCODING_UNICODE;
1742  if (LocaleCompare(encoding,"Wansung") == 0)
1743  encoding_type=FT_ENCODING_WANSUNG;
1744  ft_status=FT_Select_Charmap(face,encoding_type);
1745  if (ft_status != 0)
1746  {
1747  (void) FT_Done_Face(face);
1748  FreetypeDone(memory,library,stream);
1749  ThrowFreetypeErrorException("UnrecognizedFontEncoding",ft_status,
1750  encoding);
1751  return(MagickFalse);
1752  }
1753  }
1754  /*
1755  Set text size.
1756  */
1757  resolution.x=DefaultResolution;
1758  resolution.y=DefaultResolution;
1759  if (draw_info->density != (char *) NULL)
1760  {
1761  GeometryInfo
1762  geometry_info;
1763 
1764  MagickStatusType
1765  geometry_flags;
1766 
1767  geometry_flags=ParseGeometry(draw_info->density,&geometry_info);
1768  if ((geometry_flags & RhoValue) != 0)
1769  resolution.x=geometry_info.rho;
1770  resolution.y=resolution.x;
1771  if ((geometry_flags & SigmaValue) != 0)
1772  resolution.y=geometry_info.sigma;
1773  }
1774  ft_status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1775  (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1776  (FT_UInt) resolution.y);
1777  if (ft_status != 0)
1778  {
1779  (void) FT_Done_Face(face);
1780  FreetypeDone(memory,library,stream);
1781  ThrowFreetypeErrorException("UnableToReadFont",ft_status,
1782  draw_info->font);
1783  return(MagickFalse);
1784  }
1785  metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1786  metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1787  metrics->ascent=(double) face->size->metrics.ascender/64.0;
1788  metrics->descent=(double) face->size->metrics.descender/64.0;
1789  if (face->size->metrics.ascender == 0)
1790  {
1791  /*
1792  Sanitize buggy ascender and descender values.
1793  */
1794  metrics->ascent=face->size->metrics.y_ppem;
1795  if (face->size->metrics.descender == 0)
1796  metrics->descent=face->size->metrics.y_ppem/-3.5;
1797  }
1798  metrics->width=0;
1799  metrics->origin.x=0;
1800  metrics->origin.y=0;
1801  metrics->height=(double) face->size->metrics.height/64.0;
1802  metrics->max_advance=0.0;
1803  if (face->size->metrics.max_advance > MagickEpsilon)
1804  metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1805  metrics->bounds.x1=0.0;
1806  metrics->bounds.y1=metrics->descent;
1807  metrics->bounds.x2=metrics->ascent+metrics->descent;
1808  metrics->bounds.y2=metrics->ascent+metrics->descent;
1809  metrics->underline_position=face->underline_position*
1810  (metrics->pixels_per_em.x*MagickSafeReciprocal(face->units_per_EM));
1811  metrics->underline_thickness=face->underline_thickness*
1812  (metrics->pixels_per_em.x*MagickSafeReciprocal(face->units_per_EM));
1813  first_glyph_id=0;
1814  FT_Get_First_Char(face,&first_glyph_id);
1815  if ((draw_info->text == (char *) NULL) || (*draw_info->text == '\0') ||
1816  (first_glyph_id == 0))
1817  {
1818  (void) FT_Done_Face(face);
1819  FreetypeDone(memory,library,stream);
1820  return(MagickTrue);
1821  }
1822  /*
1823  Compute bounding box.
1824  */
1825  if (draw_info->debug != MagickFalse)
1826  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
1827  "font-encoding %s; text-encoding %s; pointsize %g",
1828  draw_info->font != (char *) NULL ? draw_info->font : "none",
1829  encoding != (char *) NULL ? encoding : "none",
1830  draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1831  draw_info->pointsize);
1832  flags=FT_LOAD_DEFAULT;
1833  if (draw_info->render == MagickFalse)
1834  flags=FT_LOAD_NO_BITMAP;
1835  if (draw_info->text_antialias == MagickFalse)
1836  flags|=FT_LOAD_TARGET_MONO;
1837  else
1838  {
1839 #if defined(FT_LOAD_TARGET_LIGHT)
1840  flags|=FT_LOAD_TARGET_LIGHT;
1841 #elif defined(FT_LOAD_TARGET_LCD)
1842  flags|=FT_LOAD_TARGET_LCD;
1843 #endif
1844  }
1845  value=GetImageProperty(image,"type:hinting",exception);
1846  if ((value != (const char *) NULL) && (LocaleCompare(value,"off") == 0))
1847  flags|=FT_LOAD_NO_HINTING;
1848  glyph.id=0;
1849  glyph.image=(FT_Glyph) NULL;
1850  last_glyph_id=0;
1851  origin.x=0;
1852  origin.y=0;
1853  affine.xx=65536L;
1854  affine.yx=0L;
1855  affine.xy=0L;
1856  affine.yy=65536L;
1857  if (draw_info->render != MagickFalse)
1858  {
1859  affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1860  affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1861  affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1862  affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1863  }
1864  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1865  if (annotate_info->dash_pattern != (double *) NULL)
1866  annotate_info->dash_pattern[0]=0.0;
1867  (void) CloneString(&annotate_info->primitive,"path '");
1868  status=MagickTrue;
1869  if (draw_info->render != MagickFalse)
1870  {
1871  if (image->storage_class != DirectClass)
1872  (void) SetImageStorageClass(image,DirectClass,exception);
1873  if ((image->alpha_trait & BlendPixelTrait) == 0)
1874  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1875  }
1876  for (p=draw_info->text; GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p))
1877  if (GetUTFCode(p) < 0)
1878  break;
1879  utf8=(unsigned char *) NULL;
1880  if (GetUTFCode(p) == 0)
1881  p=draw_info->text;
1882  else
1883  {
1884  utf8=ConvertLatin1ToUTF8((unsigned char *) draw_info->text);
1885  if (utf8 != (unsigned char *) NULL)
1886  p=(char *) utf8;
1887  }
1888  grapheme=(GraphemeInfo *) NULL;
1889  length=strlen(p);
1890 #if defined(MAGICKCORE_RAQM_DELEGATE)
1891  if (ContainsRaqmParagraphSeparator((const unsigned char *) p,length) == MagickFalse)
1892  length=ComplexRaqmTextLayout(image,draw_info,p,length,face,&grapheme,
1893  exception);
1894  else
1895 #endif
1896  length=ComplexTextLayout(draw_info,p,length,face,flags,&grapheme);
1897  missing_glyph_id=FT_Get_Char_Index(face,' ');
1898  code=0;
1899  last_character=(ssize_t) length-1;
1900  for (i=0; i < (ssize_t) length; i++)
1901  {
1902  FT_Outline
1903  outline;
1904 
1905  /*
1906  Render UTF-8 sequence.
1907  */
1908  glyph.id=(FT_UInt) grapheme[i].index;
1909  if (glyph.id == 0)
1910  glyph.id=missing_glyph_id;
1911  if ((glyph.id != 0) && (last_glyph_id != 0))
1912  origin.x+=(FT_Pos) (64.0*draw_info->kerning);
1913  glyph.origin=origin;
1914  glyph.origin.x+=(FT_Pos) grapheme[i].x_offset;
1915  glyph.origin.y+=(FT_Pos) grapheme[i].y_offset;
1916  if (glyph.image != (FT_Glyph) NULL)
1917  {
1918  FT_Done_Glyph(glyph.image);
1919  glyph.image=(FT_Glyph) NULL;
1920  }
1921  ft_status=FT_Load_Glyph(face,glyph.id,flags);
1922  if (ft_status != 0)
1923  continue;
1924  ft_status=FT_Get_Glyph(face->glyph,&glyph.image);
1925  if (ft_status != 0)
1926  continue;
1927  outline=((FT_OutlineGlyph) glyph.image)->outline;
1928  if ((glyph.image->format != FT_GLYPH_FORMAT_OUTLINE) &&
1929  (IsEmptyOutline(outline) == MagickFalse))
1930  continue;
1931  ft_status=FT_Outline_Get_BBox(&outline,&bounds);
1932  if (ft_status != 0)
1933  continue;
1934  if ((bounds.xMin < metrics->bounds.x1) && (bounds.xMin != 0))
1935  metrics->bounds.x1=(double) bounds.xMin;
1936  if ((bounds.yMin < metrics->bounds.y1) && (bounds.yMin != 0))
1937  metrics->bounds.y1=(double) bounds.yMin;
1938  if ((bounds.xMax > metrics->bounds.x2) && (bounds.xMax != 0))
1939  metrics->bounds.x2=(double) bounds.xMax;
1940  if ((bounds.yMax > metrics->bounds.y2) && (bounds.yMax != 0))
1941  metrics->bounds.y2=(double) bounds.yMax;
1942  if (((draw_info->stroke.alpha != (MagickRealType) TransparentAlpha) ||
1943  (draw_info->stroke_pattern != (Image *) NULL)) &&
1944  ((status != MagickFalse) && (draw_info->render != MagickFalse)))
1945  {
1946  /*
1947  Trace the glyph.
1948  */
1949  annotate_info->affine.tx=glyph.origin.x/64.0;
1950  annotate_info->affine.ty=(-glyph.origin.y/64.0);
1951  if (IsEmptyOutline(outline) == MagickFalse)
1952  ft_status=FT_Outline_Decompose(&outline,&OutlineMethods,
1953  annotate_info);
1954  }
1955  FT_Vector_Transform(&glyph.origin,&affine);
1956  (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1957  ft_status=FT_Glyph_To_Bitmap(&glyph.image,FT_RENDER_MODE_NORMAL,
1958  (FT_Vector *) NULL,MagickTrue);
1959  if (ft_status != 0)
1960  continue;
1961  bitmap=(FT_BitmapGlyph) glyph.image;
1962  point.x=offset->x+bitmap->left;
1963  if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1964  point.x+=(origin.x/64.0);
1965  point.y=offset->y-bitmap->top;
1966  if (draw_info->render != MagickFalse)
1967  {
1968  CacheView
1969  *image_view;
1970 
1971  MagickBooleanType
1972  transparent_fill;
1973 
1974  unsigned char
1975  *r;
1976 
1977  /*
1978  Rasterize the glyph.
1979  */
1980  transparent_fill=((draw_info->fill.alpha == (MagickRealType) TransparentAlpha) &&
1981  (draw_info->fill_pattern == (Image *) NULL) &&
1982  (draw_info->stroke.alpha == (MagickRealType) TransparentAlpha) &&
1983  (draw_info->stroke_pattern == (Image *) NULL)) ? MagickTrue :
1984  MagickFalse;
1985  r=bitmap->bitmap.buffer;
1986  image_view=AcquireAuthenticCacheView(image,exception);
1987 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1988  #pragma omp parallel for schedule(static) shared(status) \
1989  magick_number_threads(image,image,bitmap->bitmap.rows,4)
1990 #endif
1991  for (y=0; y < (ssize_t) bitmap->bitmap.rows; y++)
1992  {
1993  double
1994  fill_opacity;
1995 
1996  MagickBooleanType
1997  active,
1998  sync;
1999 
2000  PixelInfo
2001  fill_color;
2002 
2003  Quantum
2004  *magick_restrict q;
2005 
2006  ssize_t
2007  x;
2008 
2009  ssize_t
2010  n,
2011  x_offset,
2012  y_offset;
2013 
2014  if (status == MagickFalse)
2015  continue;
2016  x_offset=CastDoubleToSsizeT(ceil(point.x-0.5));
2017  y_offset=CastDoubleToSsizeT(ceil(point.y+y-0.5));
2018  if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
2019  continue;
2020  q=(Quantum *) NULL;
2021  if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
2022  active=MagickFalse;
2023  else
2024  {
2025  q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
2026  bitmap->bitmap.width,1,exception);
2027  active=q != (Quantum *) NULL ? MagickTrue : MagickFalse;
2028  }
2029  n=y*bitmap->bitmap.pitch;
2030  for (x=0; x < (ssize_t) bitmap->bitmap.width; x++, n++)
2031  {
2032  x_offset=CastDoubleToSsizeT(ceil(point.x+x-0.5));
2033  if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
2034  {
2035  if (q != (Quantum *) NULL)
2036  q+=(ptrdiff_t) GetPixelChannels(image);
2037  continue;
2038  }
2039  fill_opacity=1.0;
2040  if (bitmap->bitmap.buffer != (unsigned char *) NULL)
2041  {
2042  if (bitmap->bitmap.pixel_mode == ft_pixel_mode_grays)
2043  fill_opacity=(double) (r[n])/(bitmap->bitmap.num_grays-1);
2044  else
2045  if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
2046  fill_opacity=((r[(x >> 3)+y*bitmap->bitmap.pitch] &
2047  (1 << (~x & 0x07)))) == 0 ? 0.0 : 1.0;
2048  }
2049  if (draw_info->text_antialias == MagickFalse)
2050  fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
2051  if (active == MagickFalse)
2052  q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
2053  exception);
2054  if (q == (Quantum *) NULL)
2055  continue;
2056  if (transparent_fill == MagickFalse)
2057  {
2058  GetPixelInfo(image,&fill_color);
2059  GetFillColor(draw_info,x_offset,y_offset,&fill_color,exception);
2060  fill_opacity=fill_opacity*fill_color.alpha;
2061  CompositePixelOver(image,&fill_color,fill_opacity,q,
2062  GetPixelAlpha(image,q),q);
2063  }
2064  else
2065  {
2066  double
2067  Sa,
2068  Da;
2069 
2070  Da=1.0-(QuantumScale*(double) GetPixelAlpha(image,q));
2071  Sa=fill_opacity;
2072  fill_opacity=(1.0-RoundToUnity(Sa+Da-Sa*Da))*(double)
2073  QuantumRange;
2074  SetPixelAlpha(image,(const Quantum) fill_opacity,q);
2075  }
2076  if (active == MagickFalse)
2077  {
2078  sync=SyncCacheViewAuthenticPixels(image_view,exception);
2079  if (sync == MagickFalse)
2080  status=MagickFalse;
2081  }
2082  q+=(ptrdiff_t) GetPixelChannels(image);
2083  }
2084  sync=SyncCacheViewAuthenticPixels(image_view,exception);
2085  if (sync == MagickFalse)
2086  status=MagickFalse;
2087  }
2088  image_view=DestroyCacheView(image_view);
2089  if (((draw_info->stroke.alpha != (MagickRealType) TransparentAlpha) ||
2090  (draw_info->stroke_pattern != (Image *) NULL)) &&
2091  (status != MagickFalse))
2092  {
2093  /*
2094  Draw text stroke.
2095  */
2096  annotate_info->linejoin=RoundJoin;
2097  annotate_info->affine.tx=offset->x;
2098  annotate_info->affine.ty=offset->y;
2099  (void) ConcatenateString(&annotate_info->primitive,"'");
2100  if (strlen(annotate_info->primitive) > 7)
2101  (void) DrawImage(image,annotate_info,exception);
2102  (void) CloneString(&annotate_info->primitive,"path '");
2103  }
2104  }
2105  if ((fabs(draw_info->interword_spacing) >= MagickEpsilon) &&
2106  (IsUTFSpace(GetUTFCode(p+grapheme[i].cluster)) != MagickFalse) &&
2107  (IsUTFSpace((int) code) == MagickFalse))
2108  origin.x+=(FT_Pos) (64.0*draw_info->interword_spacing);
2109  else
2110  if (i == last_character)
2111  origin.x+=MagickMax((FT_Pos) grapheme[i].x_advance,bounds.xMax);
2112  else
2113  origin.x+=(FT_Pos) grapheme[i].x_advance;
2114  origin.y+=(FT_Pos) grapheme[i].y_advance;
2115  metrics->origin.x=(double) origin.x;
2116  metrics->origin.y=(double) origin.y;
2117  if (metrics->origin.x > metrics->width)
2118  metrics->width=metrics->origin.x;
2119  last_glyph_id=glyph.id;
2120  code=GetUTFCode(p+grapheme[i].cluster);
2121  }
2122  if (grapheme != (GraphemeInfo *) NULL)
2123  grapheme=(GraphemeInfo *) RelinquishMagickMemory(grapheme);
2124  if (utf8 != (unsigned char *) NULL)
2125  utf8=(unsigned char *) RelinquishMagickMemory(utf8);
2126  if (glyph.image != (FT_Glyph) NULL)
2127  {
2128  FT_Done_Glyph(glyph.image);
2129  glyph.image=(FT_Glyph) NULL;
2130  }
2131  /*
2132  Determine font metrics.
2133  */
2134  metrics->bounds.x1/=64.0;
2135  metrics->bounds.y1/=64.0;
2136  metrics->bounds.x2/=64.0;
2137  metrics->bounds.y2/=64.0;
2138  metrics->origin.x/=64.0;
2139  metrics->origin.y/=64.0;
2140  metrics->width=ceil(metrics->width/64.0);
2141  /*
2142  Relinquish resources.
2143  */
2144  annotate_info=DestroyDrawInfo(annotate_info);
2145  (void) FT_Done_Face(face);
2146  FreetypeDone(memory,library,stream);
2147  return(status);
2148 }
2149 #else
2150 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
2151  const char *magick_unused(encoding),const PointInfo *offset,
2152  TypeMetric *metrics,ExceptionInfo *exception)
2153 {
2154  magick_unreferenced(encoding);
2155  (void) ThrowMagickException(exception,GetMagickModule(),
2156  MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (Freetype)",
2157  draw_info->font != (char *) NULL ? draw_info->font : "none");
2158  return(RenderPostscript(image,draw_info,offset,metrics,exception));
2159 }
2160 #endif
2161 ␌
2162 /*
2163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2164 % %
2165 % %
2166 % %
2167 + R e n d e r P o s t s c r i p t %
2168 % %
2169 % %
2170 % %
2171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2172 %
2173 % RenderPostscript() renders text on the image with a Postscript font. It
2174 % also returns the bounding box of the text relative to the image.
2175 %
2176 % The format of the RenderPostscript method is:
2177 %
2178 % MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
2179 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2180 %
2181 % A description of each parameter follows:
2182 %
2183 % o image: the image.
2184 %
2185 % o draw_info: the draw info.
2186 %
2187 % o offset: (x,y) location of text relative to image.
2188 %
2189 % o metrics: bounding box of text.
2190 %
2191 % o exception: return any errors or warnings in this structure.
2192 %
2193 */
2194 
2195 static char *EscapeParenthesis(const char *source)
2196 {
2197  char
2198  *destination;
2199 
2200  char
2201  *q;
2202 
2203  const char
2204  *p;
2205 
2206  size_t
2207  length;
2208 
2209  assert(source != (const char *) NULL);
2210  length=0;
2211  for (p=source; *p != '\0'; p++)
2212  {
2213  if ((*p == '\\') || (*p == '(') || (*p == ')'))
2214  {
2215  if (~length < 1)
2216  ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
2217  length++;
2218  }
2219  length++;
2220  }
2221  destination=(char *) NULL;
2222  if (~length >= (MagickPathExtent-1))
2223  destination=(char *) AcquireQuantumMemory(length+MagickPathExtent,
2224  sizeof(*destination));
2225  if (destination == (char *) NULL)
2226  ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
2227  *destination='\0';
2228  q=destination;
2229  for (p=source; *p != '\0'; p++)
2230  {
2231  if ((*p == '\\') || (*p == '(') || (*p == ')'))
2232  *q++='\\';
2233  *q++=(*p);
2234  }
2235  *q='\0';
2236  return(destination);
2237 }
2238 
2239 static MagickBooleanType RenderPostscript(Image *image,
2240  const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics,
2241  ExceptionInfo *exception)
2242 {
2243  char
2244  filename[MagickPathExtent],
2245  geometry[MagickPathExtent],
2246  *text;
2247 
2248  FILE
2249  *file;
2250 
2251  Image
2252  *annotate_image;
2253 
2254  ImageInfo
2255  *annotate_info;
2256 
2257  int
2258  unique_file;
2259 
2260  MagickBooleanType
2261  identity,
2262  status;
2263 
2264  PointInfo
2265  extent,
2266  point,
2267  resolution;
2268 
2269  ssize_t
2270  i;
2271 
2272  size_t
2273  length;
2274 
2275  ssize_t
2276  y;
2277 
2278  /*
2279  Render label with a Postscript font.
2280  */
2281  if (draw_info->debug != MagickFalse)
2282  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
2283  "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
2284  draw_info->font : "none",draw_info->pointsize);
2285  file=(FILE *) NULL;
2286  unique_file=AcquireUniqueFileResource(filename);
2287  if (unique_file != -1)
2288  file=fdopen(unique_file,"wb");
2289  if ((unique_file == -1) || (file == (FILE *) NULL))
2290  {
2291  ThrowFileException(exception,FileOpenError,"UnableToOpenFile",filename);
2292  return(MagickFalse);
2293  }
2294  (void) FormatLocaleFile(file,"%%!PS-Adobe-3.0\n");
2295  (void) FormatLocaleFile(file,"/ReencodeType\n");
2296  (void) FormatLocaleFile(file,"{\n");
2297  (void) FormatLocaleFile(file," findfont dup length\n");
2298  (void) FormatLocaleFile(file,
2299  " dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
2300  (void) FormatLocaleFile(file,
2301  " /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
2302  (void) FormatLocaleFile(file,"} bind def\n");
2303  /*
2304  Sample to compute bounding box.
2305  */
2306  identity=(fabs(draw_info->affine.sx-draw_info->affine.sy) < MagickEpsilon) &&
2307  (fabs(draw_info->affine.rx) < MagickEpsilon) &&
2308  (fabs(draw_info->affine.ry) < MagickEpsilon) ? MagickTrue : MagickFalse;
2309  extent.x=0.0;
2310  extent.y=0.0;
2311  length=strlen(draw_info->text);
2312  for (i=0; i <= (ssize_t) (length+2); i++)
2313  {
2314  point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
2315  draw_info->affine.ry*2.0*draw_info->pointsize);
2316  point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
2317  draw_info->affine.sy*2.0*draw_info->pointsize);
2318  if (point.x > extent.x)
2319  extent.x=point.x;
2320  if (point.y > extent.y)
2321  extent.y=point.y;
2322  }
2323  (void) FormatLocaleFile(file,"%g %g moveto\n",identity != MagickFalse ? 0.0 :
2324  extent.x/2.0,extent.y/2.0);
2325  (void) FormatLocaleFile(file,"%g %g scale\n",draw_info->pointsize,
2326  draw_info->pointsize);
2327  if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
2328  (strchr(draw_info->font,'/') != (char *) NULL))
2329  (void) FormatLocaleFile(file,
2330  "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
2331  else
2332  (void) FormatLocaleFile(file,
2333  "/%s-ISO dup /%s ReencodeType findfont setfont\n",draw_info->font,
2334  draw_info->font);
2335  (void) FormatLocaleFile(file,"[%g %g %g %g 0 0] concat\n",
2336  draw_info->affine.sx,-draw_info->affine.rx,-draw_info->affine.ry,
2337  draw_info->affine.sy);
2338  text=EscapeParenthesis(draw_info->text);
2339  if (identity == MagickFalse)
2340  (void) FormatLocaleFile(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",
2341  text);
2342  (void) FormatLocaleFile(file,"(%s) show\n",text);
2343  text=DestroyString(text);
2344  (void) FormatLocaleFile(file,"showpage\n");
2345  (void) fclose(file);
2346  (void) FormatLocaleString(geometry,MagickPathExtent,"%.17gx%.17g+0+0!",
2347  floor(extent.x+0.5),floor(extent.y+0.5));
2348  annotate_info=AcquireImageInfo();
2349  (void) FormatLocaleString(annotate_info->filename,MagickPathExtent,"ps:%s",
2350  filename);
2351  (void) CloneString(&annotate_info->page,geometry);
2352  if (draw_info->density != (char *) NULL)
2353  (void) CloneString(&annotate_info->density,draw_info->density);
2354  annotate_info->antialias=draw_info->text_antialias;
2355  annotate_image=ReadImage(annotate_info,exception);
2356  CatchException(exception);
2357  annotate_info=DestroyImageInfo(annotate_info);
2358  (void) RelinquishUniqueFileResource(filename);
2359  if (annotate_image == (Image *) NULL)
2360  return(MagickFalse);
2361  (void) NegateImage(annotate_image,MagickFalse,exception);
2362  resolution.x=DefaultResolution;
2363  resolution.y=DefaultResolution;
2364  if (draw_info->density != (char *) NULL)
2365  {
2366  GeometryInfo
2367  geometry_info;
2368 
2369  MagickStatusType
2370  flags;
2371 
2372  flags=ParseGeometry(draw_info->density,&geometry_info);
2373  if ((flags & RhoValue) != 0)
2374  resolution.x=geometry_info.rho;
2375  resolution.y=resolution.x;
2376  if ((flags & SigmaValue) != 0)
2377  resolution.y=geometry_info.sigma;
2378  }
2379  if (identity == MagickFalse)
2380  (void) TransformImage(&annotate_image,"0x0",(char *) NULL,exception);
2381  else
2382  {
2384  crop_info;
2385 
2386  crop_info=GetImageBoundingBox(annotate_image,exception);
2387  crop_info.height=(size_t) ((resolution.y/DefaultResolution)*
2388  ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
2389  crop_info.y=CastDoubleToSsizeT(ceil((resolution.y/DefaultResolution)*
2390  extent.y/8.0-0.5));
2391  (void) FormatLocaleString(geometry,MagickPathExtent,
2392  "%.17gx%.17g%+.20g%+.20g",(double) crop_info.width,(double)
2393  crop_info.height,(double) crop_info.x,(double) crop_info.y);
2394  (void) TransformImage(&annotate_image,geometry,(char *) NULL,exception);
2395  }
2396  metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
2397  ExpandAffine(&draw_info->affine)*draw_info->pointsize;
2398  metrics->pixels_per_em.y=metrics->pixels_per_em.x;
2399  metrics->ascent=metrics->pixels_per_em.y;
2400  metrics->descent=metrics->pixels_per_em.y/-5.0;
2401  metrics->width=(double) annotate_image->columns/
2402  ExpandAffine(&draw_info->affine);
2403  metrics->height=floor(metrics->ascent-metrics->descent+0.5);
2404  metrics->max_advance=metrics->pixels_per_em.x;
2405  metrics->bounds.x1=0.0;
2406  metrics->bounds.y1=metrics->descent;
2407  metrics->bounds.x2=metrics->ascent+metrics->descent;
2408  metrics->bounds.y2=metrics->ascent+metrics->descent;
2409  metrics->underline_position=(-2.0);
2410  metrics->underline_thickness=1.0;
2411  if (draw_info->render == MagickFalse)
2412  {
2413  annotate_image=DestroyImage(annotate_image);
2414  return(MagickTrue);
2415  }
2416  if (draw_info->fill.alpha != (MagickRealType) TransparentAlpha)
2417  {
2418  CacheView
2419  *annotate_view;
2420 
2421  MagickBooleanType
2422  sync;
2423 
2424  PixelInfo
2425  fill_color;
2426 
2427  /*
2428  Render fill color.
2429  */
2430  if ((image->alpha_trait & BlendPixelTrait) == 0)
2431  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2432  if (annotate_image->alpha_trait == UndefinedPixelTrait)
2433  (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel,
2434  exception);
2435  fill_color=draw_info->fill;
2436  status=MagickTrue;
2437  annotate_view=AcquireAuthenticCacheView(annotate_image,exception);
2438 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2439  #pragma omp parallel for schedule(static) shared(status) \
2440  magick_number_threads(annotate_image,annotate_image,annotate_image->rows,4)
2441 #endif
2442  for (y=0; y < (ssize_t) annotate_image->rows; y++)
2443  {
2444  ssize_t
2445  x;
2446 
2447  Quantum
2448  *magick_restrict q;
2449 
2450  if (status == MagickFalse)
2451  continue;
2452  q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
2453  1,exception);
2454  if (q == (Quantum *) NULL)
2455  {
2456  status=MagickFalse;
2457  continue;
2458  }
2459  for (x=0; x < (ssize_t) annotate_image->columns; x++)
2460  {
2461  GetFillColor(draw_info,x,y,&fill_color,exception);
2462  SetPixelAlpha(annotate_image,ClampToQuantum((((double) QuantumScale*
2463  GetPixelIntensity(annotate_image,q)*fill_color.alpha))),q);
2464  SetPixelRed(annotate_image,(const Quantum) fill_color.red,q);
2465  SetPixelGreen(annotate_image,(const Quantum) fill_color.green,q);
2466  SetPixelBlue(annotate_image,(const Quantum) fill_color.blue,q);
2467  q+=(ptrdiff_t) GetPixelChannels(annotate_image);
2468  }
2469  sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
2470  if (sync == MagickFalse)
2471  status=MagickFalse;
2472  }
2473  annotate_view=DestroyCacheView(annotate_view);
2474  (void) CompositeImage(image,annotate_image,OverCompositeOp,MagickTrue,
2475  (ssize_t) ceil(offset->x-0.5),(ssize_t) ceil(offset->y-(metrics->ascent+
2476  metrics->descent)-0.5),exception);
2477  }
2478  annotate_image=DestroyImage(annotate_image);
2479  return(MagickTrue);
2480 }
2481 ␌
2482 /*
2483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2484 % %
2485 % %
2486 % %
2487 + R e n d e r X 1 1 %
2488 % %
2489 % %
2490 % %
2491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2492 %
2493 % RenderX11() renders text on the image with an X11 font. It also returns the
2494 % bounding box of the text relative to the image.
2495 %
2496 % The format of the RenderX11 method is:
2497 %
2498 % MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
2499 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2500 %
2501 % A description of each parameter follows:
2502 %
2503 % o image: the image.
2504 %
2505 % o draw_info: the draw info.
2506 %
2507 % o offset: (x,y) location of text relative to image.
2508 %
2509 % o metrics: bounding box of text.
2510 %
2511 % o exception: return any errors or warnings in this structure.
2512 %
2513 */
2514 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
2515  const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2516 {
2517  MagickBooleanType
2518  status;
2519 
2520  if (annotate_semaphore == (SemaphoreInfo *) NULL)
2521  ActivateSemaphoreInfo(&annotate_semaphore);
2522  LockSemaphoreInfo(annotate_semaphore);
2523  status=XRenderImage(image,draw_info,offset,metrics,exception);
2524  UnlockSemaphoreInfo(annotate_semaphore);
2525  return(status);
2526 }
Definition: image.h:132
Definition: type.h:51