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