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