MagickCore  6.9.12-67
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-2021 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=(size_t) floor(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  octets=(size_t) GetUTFOctets(space);
584  if (octets == 1)
585  *space='\n';
586  else
587  {
588  char
589  *target;
590 
591  size_t
592  length,
593  offset;
594 
595  length=strlen(*caption);
596  *space='\n';
597  offset=space-(*caption);
598  target=AcquireString(*caption);
599  CopyMagickString(target,*caption,offset+2);
600  ConcatenateMagickString(target,space+octets,length);
601  (void) DestroyString(*caption);
602  *caption=target;
603  space=(*caption)+offset;
604  }
605  return(space);
606 }
607 
608 MagickExport ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
609  const MagickBooleanType split,TypeMetric *metrics,char **caption)
610 {
611  MagickBooleanType
612  status;
613 
614  char
615  *p,
616  *q,
617  *s;
618 
619  ssize_t
620  i;
621 
622  size_t
623  width;
624 
625  ssize_t
626  n;
627 
628  q=draw_info->text;
629  s=(char *) NULL;
630  width=0;
631  for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
632  {
633  int
634  code;
635 
636  code=GetUTFCode(p);
637  if (code == '\n')
638  {
639  q=draw_info->text;
640  continue;
641  }
642  if ((IsUTFSpace(code) != MagickFalse) &&
643  (IsNonBreakingUTFSpace(code) == MagickFalse))
644  {
645  s=p;
646  if (width > image->columns)
647  p=ReplaceSpaceWithNewline(caption,s);
648  }
649  for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
650  *q++=(*(p+i));
651  *q='\0';
652  status=GetTypeMetrics(image,draw_info,metrics);
653  if (status == MagickFalse)
654  break;
655  width=(size_t) floor(metrics->width+draw_info->stroke_width+0.5);
656  if (width <= image->columns)
657  continue;
658  if (s != (char *) NULL)
659  p=ReplaceSpaceWithNewline(caption,s);
660  else
661  if ((split != MagickFalse) || (GetUTFOctets(p) > 2))
662  {
663  /*
664  No convenient line breaks-- insert newline.
665  */
666  n=p-(*caption);
667  if ((n > 0) && ((*caption)[n-1] != '\n'))
668  {
669  char
670  *target;
671 
672  target=AcquireString(*caption);
673  CopyMagickString(target,*caption,n+1);
674  ConcatenateMagickString(target,"\n",strlen(*caption)+1);
675  ConcatenateMagickString(target,p,strlen(*caption)+2);
676  (void) DestroyString(*caption);
677  *caption=target;
678  p=(*caption)+n;
679  }
680  }
681  q=draw_info->text;
682  s=(char *) NULL;
683  }
684  n=0;
685  for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
686  if (GetUTFCode(p) == '\n')
687  n++;
688  return(n);
689 }
690 
691 /*
692 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
693 % %
694 % %
695 % %
696 % G e t M u l t i l i n e T y p e M e t r i c s %
697 % %
698 % %
699 % %
700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
701 %
702 % GetMultilineTypeMetrics() returns the following information for the
703 % specified font and text:
704 %
705 % character width
706 % character height
707 % ascender
708 % descender
709 % text width
710 % text height
711 % maximum horizontal advance
712 % bounds: x1
713 % bounds: y1
714 % bounds: x2
715 % bounds: y2
716 % origin: x
717 % origin: y
718 % underline position
719 % underline thickness
720 %
721 % This method is like GetTypeMetrics() but it returns the maximum text width
722 % and height for multiple lines of text.
723 %
724 % The format of the GetMultilineTypeMetrics method is:
725 %
726 % MagickBooleanType GetMultilineTypeMetrics(Image *image,
727 % const DrawInfo *draw_info,TypeMetric *metrics)
728 %
729 % A description of each parameter follows:
730 %
731 % o image: the image.
732 %
733 % o draw_info: the draw info.
734 %
735 % o metrics: Return the font metrics in this structure.
736 %
737 */
738 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
739  const DrawInfo *draw_info,TypeMetric *metrics)
740 {
741  char
742  **textlist;
743 
744  double
745  height;
746 
747  DrawInfo
748  *annotate_info;
749 
750  MagickBooleanType
751  status;
752 
753  MagickSizeType
754  size;
755 
756  ssize_t
757  i;
758 
759  size_t
760  count;
761 
762  TypeMetric
763  extent;
764 
765  assert(image != (Image *) NULL);
766  assert(image->signature == MagickCoreSignature);
767  if (IsEventLogging() != MagickFalse)
768  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
769  assert(draw_info != (DrawInfo *) NULL);
770  assert(draw_info->text != (char *) NULL);
771  assert(draw_info->signature == MagickCoreSignature);
772  if (*draw_info->text == '\0')
773  {
774  (void) ThrowMagickException(&image->exception,GetMagickModule(),
775  OptionError,"LabelExpected","`%s'",image->filename);
776  return(MagickFalse);
777  }
778  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
779  annotate_info->text=DestroyString(annotate_info->text);
780  /*
781  Convert newlines to multiple lines of text.
782  */
783  textlist=StringToStrings(draw_info->text,&count);
784  if (textlist == (char **) NULL)
785  return(MagickFalse);
786  annotate_info->render=MagickFalse;
787  annotate_info->direction=UndefinedDirection;
788  (void) memset(metrics,0,sizeof(*metrics));
789  (void) memset(&extent,0,sizeof(extent));
790  /*
791  Find the widest of the text lines.
792  */
793  annotate_info->text=textlist[0];
794  status=GetTypeMetrics(image,annotate_info,&extent);
795  *metrics=extent;
796  height=(count*(size_t) (metrics->ascent-metrics->descent+
797  0.5)+(count-1)*draw_info->interline_spacing);
798  size=(MagickSizeType) fabs(height);
799  if (AcquireMagickResource(HeightResource,size) == MagickFalse)
800  {
801  (void) ThrowMagickException(&image->exception,GetMagickModule(),
802  ImageError,"WidthOrHeightExceedsLimit","`%s'",image->filename);
803  status=MagickFalse;
804  }
805  else
806  {
807  for (i=1; i < (ssize_t) count; i++)
808  {
809  annotate_info->text=textlist[i];
810  status=GetTypeMetrics(image,annotate_info,&extent);
811  if (status == MagickFalse)
812  break;
813  if (extent.width > metrics->width)
814  *metrics=extent;
815  size=(MagickSizeType) fabs(extent.width);
816  if (AcquireMagickResource(WidthResource,size) == MagickFalse)
817  {
818  (void) ThrowMagickException(&image->exception,GetMagickModule(),
819  ImageError,"WidthOrHeightExceedsLimit","`%s'",image->filename);
820  status=MagickFalse;
821  break;
822  }
823  }
824  metrics->height=(double) height;
825  }
826  /*
827  Relinquish resources.
828  */
829  annotate_info->text=(char *) NULL;
830  annotate_info=DestroyDrawInfo(annotate_info);
831  for (i=0; i < (ssize_t) count; i++)
832  textlist[i]=DestroyString(textlist[i]);
833  textlist=(char **) RelinquishMagickMemory(textlist);
834  return(status);
835 }
836 
837 /*
838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839 % %
840 % %
841 % %
842 % G e t T y p e M e t r i c s %
843 % %
844 % %
845 % %
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847 %
848 % GetTypeMetrics() returns the following information for the specified font
849 % and text:
850 %
851 % character width
852 % character height
853 % ascender
854 % descender
855 % text width
856 % text height
857 % maximum horizontal advance
858 % bounds: x1
859 % bounds: y1
860 % bounds: x2
861 % bounds: y2
862 % origin: x
863 % origin: y
864 % underline position
865 % underline thickness
866 %
867 % The format of the GetTypeMetrics method is:
868 %
869 % MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
870 % TypeMetric *metrics)
871 %
872 % A description of each parameter follows:
873 %
874 % o image: the image.
875 %
876 % o draw_info: the draw info.
877 %
878 % o metrics: Return the font metrics in this structure.
879 %
880 */
881 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
882  const DrawInfo *draw_info,TypeMetric *metrics)
883 {
884  DrawInfo
885  *annotate_info;
886 
887  MagickBooleanType
888  status;
889 
890  PointInfo
891  offset;
892 
893  assert(image != (Image *) NULL);
894  assert(image->signature == MagickCoreSignature);
895  if (IsEventLogging() != MagickFalse)
896  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
897  assert(draw_info != (DrawInfo *) NULL);
898  assert(draw_info->text != (char *) NULL);
899  assert(draw_info->signature == MagickCoreSignature);
900  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
901  annotate_info->render=MagickFalse;
902  annotate_info->direction=UndefinedDirection;
903  (void) memset(metrics,0,sizeof(*metrics));
904  offset.x=0.0;
905  offset.y=0.0;
906  status=RenderType(image,annotate_info,&offset,metrics);
907  if (draw_info->debug != MagickFalse)
908  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
909  "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
910  "bounds: %g,%g %g,%g; origin: %g,%g; pixels per em: %g,%g; "
911  "underline position: %g; underline thickness: %g",annotate_info->text,
912  metrics->width,metrics->height,metrics->ascent,metrics->descent,
913  metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
914  metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
915  metrics->pixels_per_em.x,metrics->pixels_per_em.y,
916  metrics->underline_position,metrics->underline_thickness);
917  annotate_info=DestroyDrawInfo(annotate_info);
918  return(status);
919 }
920 
921 /*
922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
923 % %
924 % %
925 % %
926 + R e n d e r T y p e %
927 % %
928 % %
929 % %
930 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
931 %
932 % RenderType() renders text on the image. It also returns the bounding box of
933 % the text relative to the image.
934 %
935 % The format of the RenderType method is:
936 %
937 % MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
938 % const PointInfo *offset,TypeMetric *metrics)
939 %
940 % A description of each parameter follows:
941 %
942 % o image: the image.
943 %
944 % o draw_info: the draw info.
945 %
946 % o offset: (x,y) location of text relative to image.
947 %
948 % o metrics: bounding box of text.
949 %
950 */
951 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
952  const PointInfo *offset,TypeMetric *metrics)
953 {
954  char
955  *font;
956 
957  const TypeInfo
958  *type_info;
959 
960  DrawInfo
961  *annotate_info;
962 
964  *sans_exception;
965 
966  MagickBooleanType
967  status;
968 
969  type_info=(const TypeInfo *) NULL;
970  if (draw_info->font != (char *) NULL)
971  {
972  if (*draw_info->font == '@')
973  {
974  status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
975  metrics);
976  return(status);
977  }
978  if (*draw_info->font == '-')
979  return(RenderX11(image,draw_info,offset,metrics));
980  if (*draw_info->font == '^')
981  return(RenderPostscript(image,draw_info,offset,metrics));
982  if (IsPathAccessible(draw_info->font) != MagickFalse)
983  {
984  status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
985  metrics);
986  return(status);
987  }
988  type_info=GetTypeInfo(draw_info->font,&image->exception);
989  if (type_info == (const TypeInfo *) NULL)
990  (void) ThrowMagickException(&image->exception,GetMagickModule(),
991  TypeWarning,"UnableToReadFont","`%s'",draw_info->font);
992  }
993  if ((type_info == (const TypeInfo *) NULL) &&
994  (draw_info->family != (const char *) NULL))
995  {
996  if (strpbrk(draw_info->family,",'\"") == (char *) NULL)
997  type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
998  draw_info->stretch,draw_info->weight,&image->exception);
999  if (type_info == (const TypeInfo *) NULL)
1000  {
1001  char
1002  **family;
1003 
1004  int
1005  number_families;
1006 
1007  ssize_t
1008  i;
1009 
1010  /*
1011  Parse font family list.
1012  */
1013  family=StringToArgv(draw_info->family,&number_families);
1014  for (i=1; i < (ssize_t) number_families; i++)
1015  {
1016  (void) SubstituteString(&family[i],",","");
1017  type_info=GetTypeInfoByFamily(family[i],draw_info->style,
1018  draw_info->stretch,draw_info->weight,&image->exception);
1019  if ((type_info != (const TypeInfo *) NULL) &&
1020  (LocaleCompare(family[i],type_info->family) == 0))
1021  break;
1022  }
1023  for (i=0; i < (ssize_t) number_families; i++)
1024  family[i]=DestroyString(family[i]);
1025  family=(char **) RelinquishMagickMemory(family);
1026  if (type_info == (const TypeInfo *) NULL)
1027  (void) ThrowMagickException(&image->exception,GetMagickModule(),
1028  TypeWarning,"UnableToReadFont","`%s'",draw_info->family);
1029  }
1030  }
1031  font=GetPolicyValue("system:font");
1032  if (font != (char *) NULL)
1033  {
1034  if (IsPathAccessible(font) != MagickFalse)
1035  {
1036  /*
1037  Render with default system font.
1038  */
1039  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1040  annotate_info->font=font;
1041  status=RenderFreetype(image,annotate_info,annotate_info->encoding,
1042  offset,metrics);
1043  annotate_info=DestroyDrawInfo(annotate_info);
1044  return(status);
1045  }
1046  font=DestroyString(font);
1047  }
1048  sans_exception=AcquireExceptionInfo();
1049  if (type_info == (const TypeInfo *) NULL)
1050  type_info=GetTypeInfoByFamily("Open Sans",draw_info->style,
1051  draw_info->stretch,draw_info->weight,sans_exception);
1052  if (type_info == (const TypeInfo *) NULL)
1053  type_info=GetTypeInfoByFamily("Sans Serif",draw_info->style,
1054  draw_info->stretch,draw_info->weight,sans_exception);
1055  if (type_info == (const TypeInfo *) NULL)
1056  type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
1057  draw_info->stretch,draw_info->weight,sans_exception);
1058  if (type_info == (const TypeInfo *) NULL)
1059  type_info=GetTypeInfo("*",sans_exception);
1060  sans_exception=DestroyExceptionInfo(sans_exception);
1061  if (type_info == (const TypeInfo *) NULL)
1062  {
1063  status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics);
1064  return(status);
1065  }
1066  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1067  annotate_info->face=type_info->face;
1068  if (type_info->metrics != (char *) NULL)
1069  (void) CloneString(&annotate_info->metrics,type_info->metrics);
1070  if (type_info->glyphs != (char *) NULL)
1071  (void) CloneString(&annotate_info->font,type_info->glyphs);
1072  status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics);
1073  annotate_info=DestroyDrawInfo(annotate_info);
1074  return(status);
1075 }
1076 
1077 /*
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 % %
1080 % %
1081 % %
1082 + R e n d e r F r e e t y p e %
1083 % %
1084 % %
1085 % %
1086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1087 %
1088 % RenderFreetype() renders text on the image with a Truetype font. It also
1089 % returns the bounding box of the text relative to the image.
1090 %
1091 % The format of the RenderFreetype method is:
1092 %
1093 % MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
1094 % const char *encoding,const PointInfo *offset,TypeMetric *metrics)
1095 %
1096 % A description of each parameter follows:
1097 %
1098 % o image: the image.
1099 %
1100 % o draw_info: the draw info.
1101 %
1102 % o encoding: the font encoding.
1103 %
1104 % o offset: (x,y) location of text relative to image.
1105 %
1106 % o metrics: bounding box of text.
1107 %
1108 */
1109 
1110 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
1111 
1112 static size_t ComplexTextLayout(const Image *image,const DrawInfo *draw_info,
1113  const char *text,const size_t length,const FT_Face face,const FT_Int32 flags,
1114  GraphemeInfo **grapheme)
1115 {
1116 #if defined(MAGICKCORE_RAQM_DELEGATE)
1117  const char
1118  *features;
1119 
1120  raqm_t
1121  *rq;
1122 
1123  raqm_glyph_t
1124  *glyphs;
1125 
1126  ssize_t
1127  i;
1128 
1129  size_t
1130  extent;
1131 
1132  magick_unreferenced(flags);
1133  extent=0;
1134  rq=raqm_create();
1135  if (rq == (raqm_t *) NULL)
1136  goto cleanup;
1137  if (raqm_set_text_utf8(rq,text,length) == 0)
1138  goto cleanup;
1139  if (raqm_set_par_direction(rq,(raqm_direction_t) draw_info->direction) == 0)
1140  goto cleanup;
1141  if (raqm_set_freetype_face(rq,face) == 0)
1142  goto cleanup;
1143  features=GetImageProperty(image,"type:features");
1144  if (features != (const char *) NULL)
1145  {
1146  char
1147  breaker,
1148  quote,
1149  *token;
1150 
1151  int
1152  next,
1153  status_token;
1154 
1155  TokenInfo
1156  *token_info;
1157 
1158  next=0;
1159  token_info=AcquireTokenInfo();
1160  token=AcquireString("");
1161  status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1162  &breaker,&next,&quote);
1163  while (status_token == 0)
1164  {
1165  raqm_add_font_feature(rq,token,strlen(token));
1166  status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1167  &breaker,&next,&quote);
1168  }
1169  token_info=DestroyTokenInfo(token_info);
1170  token=DestroyString(token);
1171  }
1172  if (raqm_layout(rq) == 0)
1173  goto cleanup;
1174  glyphs=raqm_get_glyphs(rq,&extent);
1175  if (glyphs == (raqm_glyph_t *) NULL)
1176  {
1177  extent=0;
1178  goto cleanup;
1179  }
1180  *grapheme=(GraphemeInfo *) AcquireQuantumMemory(extent,sizeof(**grapheme));
1181  if (*grapheme == (GraphemeInfo *) NULL)
1182  {
1183  extent=0;
1184  goto cleanup;
1185  }
1186  for (i=0; i < (ssize_t) extent; i++)
1187  {
1188  (*grapheme)[i].index=glyphs[i].index;
1189  (*grapheme)[i].x_offset=glyphs[i].x_offset;
1190  (*grapheme)[i].x_advance=glyphs[i].x_advance;
1191  (*grapheme)[i].y_offset=glyphs[i].y_offset;
1192  (*grapheme)[i].cluster=glyphs[i].cluster;
1193  }
1194 
1195 cleanup:
1196  raqm_destroy(rq);
1197  return(extent);
1198 #else
1199  const char
1200  *p;
1201 
1202  ssize_t
1203  i;
1204 
1205  ssize_t
1206  last_glyph;
1207 
1208  /*
1209  Simple layout for bi-directional text (right-to-left or left-to-right).
1210  */
1211  magick_unreferenced(image);
1212  *grapheme=(GraphemeInfo *) AcquireQuantumMemory(length+1,sizeof(**grapheme));
1213  if (*grapheme == (GraphemeInfo *) NULL)
1214  return(0);
1215  last_glyph=0;
1216  p=text;
1217  for (i=0; GetUTFCode(p) != 0; p+=GetUTFOctets(p), i++)
1218  {
1219  (*grapheme)[i].index=FT_Get_Char_Index(face,GetUTFCode(p));
1220  (*grapheme)[i].x_offset=0;
1221  (*grapheme)[i].y_offset=0;
1222  if (((*grapheme)[i].index != 0) && (last_glyph != 0))
1223  {
1224  if (FT_HAS_KERNING(face))
1225  {
1226  FT_Error
1227  ft_status;
1228 
1229  FT_Vector
1230  kerning;
1231 
1232  ft_status=FT_Get_Kerning(face,(FT_UInt) last_glyph,(FT_UInt)
1233  (*grapheme)[i].index,ft_kerning_default,&kerning);
1234  if (ft_status == 0)
1235  (*grapheme)[i-1].x_advance+=(FT_Pos) ((draw_info->direction ==
1236  RightToLeftDirection ? -1.0 : 1.0)*kerning.x);
1237  }
1238  }
1239  (void) FT_Load_Glyph(face,(*grapheme)[i].index,flags);
1240  (*grapheme)[i].x_advance=face->glyph->advance.x;
1241  (*grapheme)[i].cluster=p-text;
1242  last_glyph=(*grapheme)[i].index;
1243  }
1244  return((size_t) i);
1245 #endif
1246 }
1247 
1248 static void FTCloseStream(FT_Stream stream)
1249 {
1250  FILE *file = (FILE *) stream->descriptor.pointer;
1251  if (file != (FILE *) NULL)
1252  (void) fclose(file);
1253  stream->descriptor.pointer=NULL;
1254 }
1255 
1256 static unsigned long FTReadStream(FT_Stream stream,unsigned long offset,
1257  unsigned char *buffer,unsigned long count)
1258 {
1259  FILE *file = (FILE *) stream->descriptor.pointer;
1260  if (file == (FILE *) NULL)
1261  return(0);
1262  if (count == 0)
1263  return(0);
1264  if (fseek(file,(off_t) offset,SEEK_SET) != 0)
1265  return(0);
1266  return((unsigned long) fread(buffer,1,count,file));
1267 }
1268 
1269 static inline MagickBooleanType IsEmptyOutline(FT_Outline outline)
1270 {
1271  return((outline.n_points == 0) || (outline.n_contours <= 0) ? MagickTrue :
1272  MagickFalse);
1273 }
1274 
1275 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
1276  DrawInfo *draw_info)
1277 {
1278  AffineMatrix
1279  affine;
1280 
1281  char
1282  path[MaxTextExtent];
1283 
1284  affine=draw_info->affine;
1285  (void) FormatLocaleString(path,MaxTextExtent,"C%g,%g %g,%g %g,%g",affine.tx+
1286  p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,affine.ty-q->y/64.0,
1287  affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1288  (void) ConcatenateString(&draw_info->primitive,path);
1289  return(0);
1290 }
1291 
1292 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
1293 {
1294  AffineMatrix
1295  affine;
1296 
1297  char
1298  path[MaxTextExtent];
1299 
1300  affine=draw_info->affine;
1301  (void) FormatLocaleString(path,MaxTextExtent,"L%g,%g",affine.tx+to->x/64.0,
1302  affine.ty-to->y/64.0);
1303  (void) ConcatenateString(&draw_info->primitive,path);
1304  return(0);
1305 }
1306 
1307 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
1308 {
1309  AffineMatrix
1310  affine;
1311 
1312  char
1313  path[MaxTextExtent];
1314 
1315  affine=draw_info->affine;
1316  (void) FormatLocaleString(path,MaxTextExtent,"M%g,%g",affine.tx+to->x/64.0,
1317  affine.ty-to->y/64.0);
1318  (void) ConcatenateString(&draw_info->primitive,path);
1319  return(0);
1320 }
1321 
1322 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
1323  DrawInfo *draw_info)
1324 {
1325  AffineMatrix
1326  affine;
1327 
1328  char
1329  path[MaxTextExtent];
1330 
1331  affine=draw_info->affine;
1332  (void) FormatLocaleString(path,MaxTextExtent,"Q%g,%g %g,%g",affine.tx+
1333  control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,affine.ty-
1334  to->y/64.0);
1335  (void) ConcatenateString(&draw_info->primitive,path);
1336  return(0);
1337 }
1338 
1339 static inline const char *FreetypeErrorMessage(FT_Error ft_status)
1340 {
1341 #if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 10
1342  return(FT_Error_String(ft_status));
1343 #else
1344  magick_unreferenced(ft_status);
1345  return((const char *) NULL);
1346 #endif
1347 }
1348 
1349 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1350  const char *encoding,const PointInfo *offset,TypeMetric *metrics)
1351 {
1352 #if !defined(FT_OPEN_PATHNAME)
1353 #define FT_OPEN_PATHNAME ft_open_pathname
1354 #endif
1355 
1356 #define ThrowFreetypeErrorException(tag,ft_status,value) \
1357 { \
1358  const char \
1359  *error_string=FreetypeErrorMessage(ft_status); \
1360  if (error_string != (const char *) NULL) \
1361  (void) ThrowMagickException(exception,GetMagickModule(),TypeError, \
1362  tag,"`%s (%s)'",value, error_string); \
1363  else \
1364  (void) ThrowMagickException(exception,GetMagickModule(),TypeError, \
1365  tag,"`%s'",value); \
1366 }
1367 
1368  typedef struct _GlyphInfo
1369  {
1370  FT_UInt
1371  id;
1372 
1373  FT_Vector
1374  origin;
1375 
1376  FT_Glyph
1377  image;
1378  } GlyphInfo;
1379 
1380  char
1381  *p;
1382 
1383  const char
1384  *value;
1385 
1386  DrawInfo
1387  *annotate_info;
1388 
1390  *exception;
1391 
1392  FT_BBox
1393  bounds;
1394 
1395  FT_Encoding
1396  encoding_type;
1397 
1398  FT_Error
1399  ft_status;
1400 
1401  FT_Face
1402  face;
1403 
1404  FT_Int32
1405  flags;
1406 
1407  FT_Library
1408  library;
1409 
1410  FT_Long
1411  face_index;
1412 
1413  FT_Matrix
1414  affine;
1415 
1416  FT_Open_Args
1417  args;
1418 
1419  FT_StreamRec
1420  *stream;
1421 
1422  FT_UInt
1423  first_glyph_id,
1424  last_glyph_id,
1425  missing_glyph_id;
1426 
1427  FT_Vector
1428  origin;
1429 
1430  GlyphInfo
1431  glyph;
1432 
1433  GraphemeInfo
1434  *grapheme;
1435 
1436  MagickBooleanType
1437  status;
1438 
1439  PointInfo
1440  resolution;
1441 
1442  ssize_t
1443  i;
1444 
1445  size_t
1446  length;
1447 
1448  ssize_t
1449  code,
1450  last_character,
1451  y;
1452 
1453  static FT_Outline_Funcs
1454  OutlineMethods =
1455  {
1456  (FT_Outline_MoveTo_Func) TraceMoveTo,
1457  (FT_Outline_LineTo_Func) TraceLineTo,
1458  (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1459  (FT_Outline_CubicTo_Func) TraceCubicBezier,
1460  0, 0
1461  };
1462 
1463  struct stat
1464  attributes;
1465 
1466  unsigned char
1467  *utf8;
1468 
1469  /*
1470  Initialize Truetype library.
1471  */
1472  exception=(&image->exception);
1473  ft_status=FT_Init_FreeType(&library);
1474  if (ft_status != 0)
1475  ThrowFreetypeErrorException("UnableToInitializeFreetypeLibrary",ft_status,
1476  image->filename);
1477  face_index=(FT_Long) draw_info->face;
1478  /*
1479  Open font face.
1480  */
1481  (void) memset(&args,0,sizeof(args));
1482  if (draw_info->font == (char *) NULL)
1483  args.pathname=ConstantString("helvetica");
1484  else
1485  if (*draw_info->font != '@')
1486  args.pathname=ConstantString(draw_info->font);
1487  else
1488  {
1489  /*
1490  Extract face index, e.g. @msgothic[1].
1491  */
1492  ImageInfo *image_info=AcquireImageInfo();
1493  (void) strcpy(image_info->filename,draw_info->font+1);
1494  (void) SetImageInfo(image_info,0,exception);
1495  face_index=(FT_Long) image_info->scene;
1496  args.pathname=ConstantString(image_info->filename);
1497  image_info=DestroyImageInfo(image_info);
1498  }
1499  /*
1500  Configure streaming interface.
1501  */
1502  stream=(FT_StreamRec *) AcquireCriticalMemory(sizeof(*stream));
1503  (void) memset(stream,0,sizeof(*stream));
1504  (void) stat(args.pathname,&attributes);
1505  stream->size=attributes.st_size;
1506  stream->descriptor.pointer=fopen_utf8(args.pathname,"rb");
1507  stream->read=(&FTReadStream);
1508  stream->close=(&FTCloseStream);
1509  args.flags=FT_OPEN_STREAM;
1510  args.stream=stream;
1511  face=(FT_Face) NULL;
1512  ft_status=FT_Open_Face(library,&args,face_index,&face);
1513  if (ft_status != 0)
1514  {
1515  (void) FT_Done_FreeType(library);
1516  stream=RelinquishMagickMemory(stream);
1517  ThrowFreetypeErrorException("UnableToReadFont",ft_status,args.pathname);
1518  args.pathname=DestroyString(args.pathname);
1519  return(MagickFalse);
1520  }
1521  args.pathname=DestroyString(args.pathname);
1522  if ((draw_info->metrics != (char *) NULL) &&
1523  (IsPathAccessible(draw_info->metrics) != MagickFalse))
1524  (void) FT_Attach_File(face,draw_info->metrics);
1525  encoding_type=FT_ENCODING_UNICODE;
1526  ft_status=FT_Select_Charmap(face,encoding_type);
1527  if ((ft_status != 0) && (face->num_charmaps != 0))
1528  ft_status=FT_Set_Charmap(face,face->charmaps[0]);
1529  if (encoding != (const char *) NULL)
1530  {
1531  if (LocaleCompare(encoding,"AdobeCustom") == 0)
1532  encoding_type=FT_ENCODING_ADOBE_CUSTOM;
1533  if (LocaleCompare(encoding,"AdobeExpert") == 0)
1534  encoding_type=FT_ENCODING_ADOBE_EXPERT;
1535  if (LocaleCompare(encoding,"AdobeStandard") == 0)
1536  encoding_type=FT_ENCODING_ADOBE_STANDARD;
1537  if (LocaleCompare(encoding,"AppleRoman") == 0)
1538  encoding_type=FT_ENCODING_APPLE_ROMAN;
1539  if (LocaleCompare(encoding,"BIG5") == 0)
1540  encoding_type=FT_ENCODING_BIG5;
1541 #if defined(FT_ENCODING_PRC)
1542  if (LocaleCompare(encoding,"GB2312") == 0)
1543  encoding_type=FT_ENCODING_PRC;
1544 #endif
1545 #if defined(FT_ENCODING_JOHAB)
1546  if (LocaleCompare(encoding,"Johab") == 0)
1547  encoding_type=FT_ENCODING_JOHAB;
1548 #endif
1549 #if defined(FT_ENCODING_ADOBE_LATIN_1)
1550  if (LocaleCompare(encoding,"Latin-1") == 0)
1551  encoding_type=FT_ENCODING_ADOBE_LATIN_1;
1552 #endif
1553 #if defined(FT_ENCODING_ADOBE_LATIN_2)
1554  if (LocaleCompare(encoding,"Latin-2") == 0)
1555  encoding_type=FT_ENCODING_OLD_LATIN_2;
1556 #endif
1557  if (LocaleCompare(encoding,"None") == 0)
1558  encoding_type=FT_ENCODING_NONE;
1559  if (LocaleCompare(encoding,"SJIScode") == 0)
1560  encoding_type=FT_ENCODING_SJIS;
1561  if (LocaleCompare(encoding,"Symbol") == 0)
1562  encoding_type=FT_ENCODING_MS_SYMBOL;
1563  if (LocaleCompare(encoding,"Unicode") == 0)
1564  encoding_type=FT_ENCODING_UNICODE;
1565  if (LocaleCompare(encoding,"Wansung") == 0)
1566  encoding_type=FT_ENCODING_WANSUNG;
1567  ft_status=FT_Select_Charmap(face,encoding_type);
1568  if (ft_status != 0)
1569  {
1570  (void) FT_Done_Face(face);
1571  (void) FT_Done_FreeType(library);
1572  stream=RelinquishMagickMemory(stream);
1573  ThrowFreetypeErrorException("UnrecognizedFontEncoding",ft_status,
1574  encoding);
1575  return(MagickFalse);
1576  }
1577  }
1578  /*
1579  Set text size.
1580  */
1581  resolution.x=DefaultResolution;
1582  resolution.y=DefaultResolution;
1583  if (draw_info->density != (char *) NULL)
1584  {
1585  GeometryInfo
1586  geometry_info;
1587 
1588  MagickStatusType
1589  flags;
1590 
1591  flags=ParseGeometry(draw_info->density,&geometry_info);
1592  if ((flags & RhoValue) != 0)
1593  resolution.x=geometry_info.rho;
1594  resolution.y=resolution.x;
1595  if ((flags & SigmaValue) != 0)
1596  resolution.y=geometry_info.sigma;
1597  }
1598  ft_status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1599  (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1600  (FT_UInt) resolution.y);
1601  if (ft_status != 0)
1602  {
1603  (void) FT_Done_Face(face);
1604  (void) FT_Done_FreeType(library);
1605  stream=RelinquishMagickMemory(stream);
1606  ThrowFreetypeErrorException("UnableToReadFont",ft_status,
1607  draw_info->font);
1608  return(MagickFalse);
1609  }
1610  metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1611  metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1612  metrics->ascent=(double) face->size->metrics.ascender/64.0;
1613  metrics->descent=(double) face->size->metrics.descender/64.0;
1614  if (face->size->metrics.ascender == 0)
1615  {
1616  /*
1617  Sanitize buggy ascender and descender values.
1618  */
1619  metrics->ascent=face->size->metrics.y_ppem;
1620  if (face->size->metrics.descender == 0)
1621  metrics->descent=face->size->metrics.y_ppem/-3.5;
1622  }
1623  metrics->width=0;
1624  metrics->origin.x=0;
1625  metrics->origin.y=0;
1626  metrics->height=(double) face->size->metrics.height/64.0;
1627  metrics->max_advance=0.0;
1628  if (face->size->metrics.max_advance > MagickEpsilon)
1629  metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1630  metrics->bounds.x1=0.0;
1631  metrics->bounds.y1=metrics->descent;
1632  metrics->bounds.x2=metrics->ascent+metrics->descent;
1633  metrics->bounds.y2=metrics->ascent+metrics->descent;
1634  metrics->underline_position=face->underline_position*
1635  (metrics->pixels_per_em.x*PerceptibleReciprocal(face->units_per_EM));
1636  metrics->underline_thickness=face->underline_thickness*
1637  (metrics->pixels_per_em.x*PerceptibleReciprocal(face->units_per_EM));
1638  first_glyph_id=0;
1639  FT_Get_First_Char(face,&first_glyph_id);
1640  if ((draw_info->text == (char *) NULL) || (*draw_info->text == '\0') ||
1641  (first_glyph_id == 0))
1642  {
1643  (void) FT_Done_Face(face);
1644  (void) FT_Done_FreeType(library);
1645  stream=(FT_StreamRec *) RelinquishMagickMemory(stream);
1646  return(MagickTrue);
1647  }
1648  /*
1649  Compute bounding box.
1650  */
1651  if (draw_info->debug != MagickFalse)
1652  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
1653  "font-encoding %s; text-encoding %s; pointsize %g",
1654  draw_info->font != (char *) NULL ? draw_info->font : "none",
1655  encoding != (char *) NULL ? encoding : "none",
1656  draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1657  draw_info->pointsize);
1658  flags=FT_LOAD_DEFAULT;
1659  if (draw_info->render == MagickFalse)
1660  flags=FT_LOAD_NO_BITMAP;
1661  if (draw_info->text_antialias == MagickFalse)
1662  flags|=FT_LOAD_TARGET_MONO;
1663  else
1664  {
1665 #if defined(FT_LOAD_TARGET_LIGHT)
1666  flags|=FT_LOAD_TARGET_LIGHT;
1667 #elif defined(FT_LOAD_TARGET_LCD)
1668  flags|=FT_LOAD_TARGET_LCD;
1669 #endif
1670  }
1671  value=GetImageProperty(image,"type:hinting");
1672  if ((value != (const char *) NULL) && (LocaleCompare(value,"off") == 0))
1673  flags|=FT_LOAD_NO_HINTING;
1674  glyph.id=0;
1675  glyph.image=(FT_Glyph) NULL;
1676  last_glyph_id=0;
1677  origin.x=0;
1678  origin.y=0;
1679  affine.xx=65536L;
1680  affine.yx=0L;
1681  affine.xy=0L;
1682  affine.yy=65536L;
1683  if (draw_info->render != MagickFalse)
1684  {
1685  affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1686  affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1687  affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1688  affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1689  }
1690  annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1691  if (annotate_info->dash_pattern != (double *) NULL)
1692  annotate_info->dash_pattern[0]=0.0;
1693  (void) CloneString(&annotate_info->primitive,"path '");
1694  status=MagickTrue;
1695  if (draw_info->render != MagickFalse)
1696  {
1697  if (image->storage_class != DirectClass)
1698  (void) SetImageStorageClass(image,DirectClass);
1699  if (image->matte == MagickFalse)
1700  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1701  }
1702  for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
1703  if (GetUTFCode(p) < 0)
1704  break;
1705  utf8=(unsigned char *) NULL;
1706  if (GetUTFCode(p) == 0)
1707  p=draw_info->text;
1708  else
1709  {
1710  utf8=ConvertLatin1ToUTF8((unsigned char *) draw_info->text);
1711  if (utf8 != (unsigned char *) NULL)
1712  p=(char *) utf8;
1713  }
1714  grapheme=(GraphemeInfo *) NULL;
1715  length=ComplexTextLayout(image,draw_info,p,strlen(p),face,flags,&grapheme);
1716  missing_glyph_id=FT_Get_Char_Index(face,' ');
1717  code=0;
1718  last_character=(ssize_t) length-1;
1719  for (i=0; i < (ssize_t) length; i++)
1720  {
1721  FT_BitmapGlyph
1722  bitmap;
1723 
1724  FT_Outline
1725  outline;
1726 
1727  PointInfo
1728  point;
1729 
1730  /*
1731  Render UTF-8 sequence.
1732  */
1733  glyph.id=grapheme[i].index;
1734  if (glyph.id == 0)
1735  glyph.id=missing_glyph_id;
1736  if ((glyph.id != 0) && (last_glyph_id != 0))
1737  origin.x+=(FT_Pos) (64.0*draw_info->kerning);
1738  glyph.origin=origin;
1739  glyph.origin.x+=grapheme[i].x_offset;
1740  glyph.origin.y+=grapheme[i].y_offset;
1741  if (glyph.image != (FT_Glyph) NULL)
1742  {
1743  FT_Done_Glyph(glyph.image);
1744  glyph.image=(FT_Glyph) NULL;
1745  }
1746  ft_status=FT_Load_Glyph(face,glyph.id,flags);
1747  if (ft_status != 0)
1748  continue;
1749  ft_status=FT_Get_Glyph(face->glyph,&glyph.image);
1750  if (ft_status != 0)
1751  continue;
1752  outline=((FT_OutlineGlyph) glyph.image)->outline;
1753  if ((glyph.image->format != FT_GLYPH_FORMAT_OUTLINE) &&
1754  (IsEmptyOutline(outline) == MagickFalse))
1755  continue;
1756  ft_status=FT_Outline_Get_BBox(&outline,&bounds);
1757  if (ft_status != 0)
1758  continue;
1759  if ((bounds.xMin < metrics->bounds.x1) && (bounds.xMin != 0))
1760  metrics->bounds.x1=(double) bounds.xMin;
1761  if ((bounds.yMin < metrics->bounds.y1) && (bounds.yMin != 0))
1762  metrics->bounds.y1=(double) bounds.yMin;
1763  if ((bounds.xMax > metrics->bounds.x2) && (bounds.xMax != 0))
1764  metrics->bounds.x2=(double) bounds.xMax;
1765  if ((bounds.yMax > metrics->bounds.y2) && (bounds.yMax != 0))
1766  metrics->bounds.y2=(double) bounds.yMax;
1767  if (((draw_info->stroke.opacity != TransparentOpacity) ||
1768  (draw_info->stroke_pattern != (Image *) NULL)) &&
1769  ((status != MagickFalse) && (draw_info->render != MagickFalse)))
1770  {
1771  /*
1772  Trace the glyph.
1773  */
1774  annotate_info->affine.tx=glyph.origin.x/64.0;
1775  annotate_info->affine.ty=(-glyph.origin.y/64.0);
1776  if (IsEmptyOutline(outline) == MagickFalse)
1777  ft_status=FT_Outline_Decompose(&outline,&OutlineMethods,
1778  annotate_info);
1779  }
1780  FT_Vector_Transform(&glyph.origin,&affine);
1781  (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1782  ft_status=FT_Glyph_To_Bitmap(&glyph.image,FT_RENDER_MODE_NORMAL,
1783  (FT_Vector *) NULL,MagickTrue);
1784  if (ft_status != 0)
1785  continue;
1786  bitmap=(FT_BitmapGlyph) glyph.image;
1787  point.x=offset->x+bitmap->left;
1788  if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1789  point.x+=(origin.x/64.0);
1790  point.y=offset->y-bitmap->top;
1791  if (draw_info->render != MagickFalse)
1792  {
1793  CacheView
1794  *image_view;
1795 
1796  unsigned char
1797  *p;
1798 
1799  MagickBooleanType
1800  transparent_fill;
1801 
1802  /*
1803  Rasterize the glyph.
1804  */
1805  transparent_fill=((draw_info->fill.opacity == TransparentOpacity) &&
1806  (draw_info->fill_pattern == (Image *) NULL) &&
1807  (draw_info->stroke.opacity == TransparentOpacity) &&
1808  (draw_info->stroke_pattern == (Image *) NULL)) ? MagickTrue :
1809  MagickFalse;
1810  p=bitmap->bitmap.buffer;
1811  image_view=AcquireAuthenticCacheView(image,exception);
1812  for (y=0; y < (ssize_t) bitmap->bitmap.rows; y++)
1813  {
1814  MagickBooleanType
1815  active,
1816  sync;
1817 
1818  MagickRealType
1819  fill_opacity;
1820 
1821  PixelPacket
1822  fill_color,
1823  *magick_restrict q;
1824 
1825  ssize_t
1826  x;
1827 
1828  ssize_t
1829  n,
1830  x_offset,
1831  y_offset;
1832 
1833  if (status == MagickFalse)
1834  continue;
1835  x_offset=CastDoubleToLong(ceil(point.x-0.5));
1836  y_offset=CastDoubleToLong(ceil(point.y+y-0.5));
1837  if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
1838  continue;
1839  q=(PixelPacket *) NULL;
1840  if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1841  active=MagickFalse;
1842  else
1843  {
1844  q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
1845  bitmap->bitmap.width,1,exception);
1846  active=q != (PixelPacket *) NULL ? MagickTrue : MagickFalse;
1847  }
1848  n=y*bitmap->bitmap.pitch;
1849  for (x=0; x < (ssize_t) bitmap->bitmap.width; x++, n++)
1850  {
1851  x_offset=CastDoubleToLong(ceil(point.x+x-0.5));
1852  if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1853  {
1854  if (q != (PixelPacket *) NULL)
1855  q++;
1856  continue;
1857  }
1858  fill_opacity=1.0;
1859  if (bitmap->bitmap.buffer != (unsigned char *) NULL)
1860  {
1861  if (bitmap->bitmap.pixel_mode == ft_pixel_mode_grays)
1862  fill_opacity=(MagickRealType) (p[n])/(
1863  bitmap->bitmap.num_grays-1);
1864  else
1865  if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1866  fill_opacity=((p[(x >> 3)+y*bitmap->bitmap.pitch] &
1867  (1 << (~x & 0x07)))) == 0 ? 0.0 : 1.0;
1868  }
1869  if (draw_info->text_antialias == MagickFalse)
1870  fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
1871  if (active == MagickFalse)
1872  q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
1873  exception);
1874  if (q == (PixelPacket *) NULL)
1875  continue;
1876  if (transparent_fill == MagickFalse)
1877  {
1878  (void) GetFillColor(draw_info,x_offset,y_offset,&fill_color);
1879  fill_opacity=QuantumRange-fill_opacity*(QuantumRange-
1880  fill_color.opacity);
1881  MagickCompositeOver(&fill_color,fill_opacity,q,q->opacity,q);
1882  }
1883  else
1884  {
1885  double
1886  Sa,
1887  Da;
1888 
1889  Da=1.0-(QuantumScale*(QuantumRange-q->opacity));
1890  Sa=fill_opacity;
1891  fill_opacity=(1.0-RoundToUnity(Sa+Da-Sa*Da))*QuantumRange;
1892  SetPixelAlpha(q,fill_opacity);
1893  }
1894  if (active == MagickFalse)
1895  {
1896  sync=SyncCacheViewAuthenticPixels(image_view,exception);
1897  if (sync == MagickFalse)
1898  status=MagickFalse;
1899  }
1900  q++;
1901  }
1902  sync=SyncCacheViewAuthenticPixels(image_view,exception);
1903  if (sync == MagickFalse)
1904  status=MagickFalse;
1905  }
1906  image_view=DestroyCacheView(image_view);
1907  if (((draw_info->stroke.opacity != TransparentOpacity) ||
1908  (draw_info->stroke_pattern != (Image *) NULL)) &&
1909  (status != MagickFalse))
1910  {
1911  /*
1912  Draw text stroke.
1913  */
1914  annotate_info->linejoin=RoundJoin;
1915  annotate_info->affine.tx=offset->x;
1916  annotate_info->affine.ty=offset->y;
1917  (void) ConcatenateString(&annotate_info->primitive,"'");
1918  if (strlen(annotate_info->primitive) > 7)
1919  (void) DrawImage(image,annotate_info);
1920  (void) CloneString(&annotate_info->primitive,"path '");
1921  }
1922  }
1923  if ((fabs(draw_info->interword_spacing) >= MagickEpsilon) &&
1924  (IsUTFSpace(GetUTFCode(p+grapheme[i].cluster)) != MagickFalse) &&
1925  (IsUTFSpace(code) == MagickFalse))
1926  origin.x+=(FT_Pos) (64.0*draw_info->interword_spacing);
1927  else
1928  if (i == last_character)
1929  origin.x+=MagickMax((FT_Pos) grapheme[i].x_advance,bounds.xMax);
1930  else
1931  origin.x+=(FT_Pos) grapheme[i].x_advance;
1932  metrics->origin.x=(double) origin.x;
1933  metrics->origin.y=(double) origin.y;
1934  if (metrics->origin.x > metrics->width)
1935  metrics->width=metrics->origin.x;
1936  last_glyph_id=glyph.id;
1937  code=GetUTFCode(p+grapheme[i].cluster);
1938  }
1939  if (grapheme != (GraphemeInfo *) NULL)
1940  grapheme=(GraphemeInfo *) RelinquishMagickMemory(grapheme);
1941  if (utf8 != (unsigned char *) NULL)
1942  utf8=(unsigned char *) RelinquishMagickMemory(utf8);
1943  if (glyph.image != (FT_Glyph) NULL)
1944  {
1945  FT_Done_Glyph(glyph.image);
1946  glyph.image=(FT_Glyph) NULL;
1947  }
1948  /*
1949  Determine font metrics.
1950  */
1951  metrics->bounds.x1/=64.0;
1952  metrics->bounds.y1/=64.0;
1953  metrics->bounds.x2/=64.0;
1954  metrics->bounds.y2/=64.0;
1955  metrics->origin.x/=64.0;
1956  metrics->origin.y/=64.0;
1957  metrics->width/=64.0;
1958  /*
1959  Relinquish resources.
1960  */
1961  annotate_info=DestroyDrawInfo(annotate_info);
1962  (void) FT_Done_Face(face);
1963  (void) FT_Done_FreeType(library);
1964  stream=(FT_StreamRec *) RelinquishMagickMemory(stream);
1965  return(status);
1966 }
1967 #else
1968 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1969  const char *magick_unused(encoding),const PointInfo *offset,
1970  TypeMetric *metrics)
1971 {
1972  (void) ThrowMagickException(&image->exception,GetMagickModule(),
1973  MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (Freetype)",
1974  draw_info->font != (char *) NULL ? draw_info->font : "none");
1975  return(RenderPostscript(image,draw_info,offset,metrics));
1976 }
1977 #endif
1978 
1979 /*
1980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1981 % %
1982 % %
1983 % %
1984 + R e n d e r P o s t s c r i p t %
1985 % %
1986 % %
1987 % %
1988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1989 %
1990 % RenderPostscript() renders text on the image with a Postscript font. It
1991 % also returns the bounding box of the text relative to the image.
1992 %
1993 % The format of the RenderPostscript method is:
1994 %
1995 % MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
1996 % const PointInfo *offset,TypeMetric *metrics)
1997 %
1998 % A description of each parameter follows:
1999 %
2000 % o image: the image.
2001 %
2002 % o draw_info: the draw info.
2003 %
2004 % o offset: (x,y) location of text relative to image.
2005 %
2006 % o metrics: bounding box of text.
2007 %
2008 */
2009 
2010 static char *EscapeParenthesis(const char *source)
2011 {
2012  char
2013  *destination;
2014 
2015  char
2016  *q;
2017 
2018  const char
2019  *p;
2020 
2021  size_t
2022  length;
2023 
2024  assert(source != (const char *) NULL);
2025  length=0;
2026  for (p=source; *p != '\0'; p++)
2027  {
2028  if ((*p == '\\') || (*p == '(') || (*p == ')'))
2029  {
2030  if (~length < 1)
2031  ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
2032  length++;
2033  }
2034  length++;
2035  }
2036  destination=(char *) NULL;
2037  if (~length >= (MaxTextExtent-1))
2038  destination=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2039  sizeof(*destination));
2040  if (destination == (char *) NULL)
2041  ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
2042  *destination='\0';
2043  q=destination;
2044  for (p=source; *p != '\0'; p++)
2045  {
2046  if ((*p == '\\') || (*p == '(') || (*p == ')'))
2047  *q++='\\';
2048  *q++=(*p);
2049  }
2050  *q='\0';
2051  return(destination);
2052 }
2053 
2054 static MagickBooleanType RenderPostscript(Image *image,
2055  const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics)
2056 {
2057  char
2058  filename[MaxTextExtent],
2059  geometry[MaxTextExtent],
2060  *text;
2061 
2062  FILE
2063  *file;
2064 
2065  Image
2066  *annotate_image;
2067 
2068  ImageInfo
2069  *annotate_info;
2070 
2071  int
2072  unique_file;
2073 
2074  MagickBooleanType
2075  identity;
2076 
2077  PointInfo
2078  extent,
2079  point,
2080  resolution;
2081 
2082  ssize_t
2083  i;
2084 
2085  size_t
2086  length;
2087 
2088  ssize_t
2089  y;
2090 
2091  /*
2092  Render label with a Postscript font.
2093  */
2094  if (draw_info->debug != MagickFalse)
2095  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
2096  "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
2097  draw_info->font : "none",draw_info->pointsize);
2098  file=(FILE *) NULL;
2099  unique_file=AcquireUniqueFileResource(filename);
2100  if (unique_file != -1)
2101  file=fdopen(unique_file,"wb");
2102  if ((unique_file == -1) || (file == (FILE *) NULL))
2103  {
2104  ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
2105  filename);
2106  return(MagickFalse);
2107  }
2108  (void) FormatLocaleFile(file,"%%!PS-Adobe-3.0\n");
2109  (void) FormatLocaleFile(file,"/ReencodeType\n");
2110  (void) FormatLocaleFile(file,"{\n");
2111  (void) FormatLocaleFile(file," findfont dup length\n");
2112  (void) FormatLocaleFile(file,
2113  " dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
2114  (void) FormatLocaleFile(file,
2115  " /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
2116  (void) FormatLocaleFile(file,"} bind def\n");
2117  /*
2118  Sample to compute bounding box.
2119  */
2120  identity=(fabs(draw_info->affine.sx-draw_info->affine.sy) < MagickEpsilon) &&
2121  (fabs(draw_info->affine.rx) < MagickEpsilon) &&
2122  (fabs(draw_info->affine.ry) < MagickEpsilon) ? MagickTrue : MagickFalse;
2123  extent.x=0.0;
2124  extent.y=0.0;
2125  length=strlen(draw_info->text);
2126  for (i=0; i <= (ssize_t) (length+2); i++)
2127  {
2128  point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
2129  draw_info->affine.ry*2.0*draw_info->pointsize);
2130  point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
2131  draw_info->affine.sy*2.0*draw_info->pointsize);
2132  if (point.x > extent.x)
2133  extent.x=point.x;
2134  if (point.y > extent.y)
2135  extent.y=point.y;
2136  }
2137  (void) FormatLocaleFile(file,"%g %g moveto\n",identity != MagickFalse ? 0.0 :
2138  extent.x/2.0,extent.y/2.0);
2139  (void) FormatLocaleFile(file,"%g %g scale\n",draw_info->pointsize,
2140  draw_info->pointsize);
2141  if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
2142  (strchr(draw_info->font,'/') != (char *) NULL))
2143  (void) FormatLocaleFile(file,
2144  "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
2145  else
2146  (void) FormatLocaleFile(file,
2147  "/%s-ISO dup /%s ReencodeType findfont setfont\n",draw_info->font,
2148  draw_info->font);
2149  (void) FormatLocaleFile(file,"[%g %g %g %g 0 0] concat\n",
2150  draw_info->affine.sx,-draw_info->affine.rx,-draw_info->affine.ry,
2151  draw_info->affine.sy);
2152  text=EscapeParenthesis(draw_info->text);
2153  if (identity == MagickFalse)
2154  (void) FormatLocaleFile(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",
2155  text);
2156  (void) FormatLocaleFile(file,"(%s) show\n",text);
2157  text=DestroyString(text);
2158  (void) FormatLocaleFile(file,"showpage\n");
2159  (void) fclose(file);
2160  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0!",
2161  floor(extent.x+0.5),floor(extent.y+0.5));
2162  annotate_info=AcquireImageInfo();
2163  (void) FormatLocaleString(annotate_info->filename,MaxTextExtent,"ps:%s",
2164  filename);
2165  (void) CloneString(&annotate_info->page,geometry);
2166  if (draw_info->density != (char *) NULL)
2167  (void) CloneString(&annotate_info->density,draw_info->density);
2168  annotate_info->antialias=draw_info->text_antialias;
2169  annotate_image=ReadImage(annotate_info,&image->exception);
2170  CatchException(&image->exception);
2171  annotate_info=DestroyImageInfo(annotate_info);
2172  (void) RelinquishUniqueFileResource(filename);
2173  if (annotate_image == (Image *) NULL)
2174  return(MagickFalse);
2175  resolution.x=DefaultResolution;
2176  resolution.y=DefaultResolution;
2177  if (draw_info->density != (char *) NULL)
2178  {
2179  GeometryInfo
2180  geometry_info;
2181 
2182  MagickStatusType
2183  flags;
2184 
2185  flags=ParseGeometry(draw_info->density,&geometry_info);
2186  if ((flags & RhoValue) != 0)
2187  resolution.x=geometry_info.rho;
2188  resolution.y=resolution.x;
2189  if ((flags & SigmaValue) != 0)
2190  resolution.y=geometry_info.sigma;
2191  }
2192  if (identity == MagickFalse)
2193  (void) TransformImage(&annotate_image,"0x0",(char *) NULL);
2194  else
2195  {
2197  crop_info;
2198 
2199  crop_info=GetImageBoundingBox(annotate_image,&annotate_image->exception);
2200  crop_info.height=(size_t) ((resolution.y/DefaultResolution)*
2201  ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
2202  crop_info.y=CastDoubleToLong(ceil((resolution.y/DefaultResolution)*
2203  extent.y/8.0-0.5));
2204  (void) FormatLocaleString(geometry,MaxTextExtent,
2205  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
2206  crop_info.height,(double) crop_info.x,(double) crop_info.y);
2207  (void) TransformImage(&annotate_image,geometry,(char *) NULL);
2208  }
2209  metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
2210  ExpandAffine(&draw_info->affine)*draw_info->pointsize;
2211  metrics->pixels_per_em.y=metrics->pixels_per_em.x;
2212  metrics->ascent=metrics->pixels_per_em.y;
2213  metrics->descent=metrics->pixels_per_em.y/-5.0;
2214  metrics->width=(double) annotate_image->columns/
2215  ExpandAffine(&draw_info->affine);
2216  metrics->height=floor(metrics->ascent-metrics->descent+0.5);
2217  metrics->max_advance=metrics->pixels_per_em.x;
2218  metrics->bounds.x1=0.0;
2219  metrics->bounds.y1=metrics->descent;
2220  metrics->bounds.x2=metrics->ascent+metrics->descent;
2221  metrics->bounds.y2=metrics->ascent+metrics->descent;
2222  metrics->underline_position=(-2.0);
2223  metrics->underline_thickness=1.0;
2224  if (draw_info->render == MagickFalse)
2225  {
2226  annotate_image=DestroyImage(annotate_image);
2227  return(MagickTrue);
2228  }
2229  if (draw_info->fill.opacity != TransparentOpacity)
2230  {
2232  *exception;
2233 
2234  MagickBooleanType
2235  sync;
2236 
2237  PixelPacket
2238  fill_color;
2239 
2240  CacheView
2241  *annotate_view;
2242 
2243  /*
2244  Render fill color.
2245  */
2246  if (image->matte == MagickFalse)
2247  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
2248  if (annotate_image->matte == MagickFalse)
2249  (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel);
2250  fill_color=draw_info->fill;
2251  exception=(&image->exception);
2252  annotate_view=AcquireAuthenticCacheView(annotate_image,exception);
2253  for (y=0; y < (ssize_t) annotate_image->rows; y++)
2254  {
2255  PixelPacket
2256  *magick_restrict q;
2257 
2258  ssize_t
2259  x;
2260 
2261  q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
2262  1,exception);
2263  if (q == (PixelPacket *) NULL)
2264  break;
2265  for (x=0; x < (ssize_t) annotate_image->columns; x++)
2266  {
2267  (void) GetFillColor(draw_info,x,y,&fill_color);
2268  SetPixelAlpha(q,ClampToQuantum((((QuantumRange-GetPixelIntensity(
2269  annotate_image,q))*(QuantumRange-fill_color.opacity))/
2270  QuantumRange)));
2271  SetPixelRed(q,fill_color.red);
2272  SetPixelGreen(q,fill_color.green);
2273  SetPixelBlue(q,fill_color.blue);
2274  q++;
2275  }
2276  sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
2277  if (sync == MagickFalse)
2278  break;
2279  }
2280  annotate_view=DestroyCacheView(annotate_view);
2281  (void) CompositeImage(image,OverCompositeOp,annotate_image,
2282  (ssize_t) ceil(offset->x-0.5),(ssize_t) ceil(offset->y-(metrics->ascent+
2283  metrics->descent)-0.5));
2284  }
2285  annotate_image=DestroyImage(annotate_image);
2286  return(MagickTrue);
2287 }
2288 
2289 /*
2290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2291 % %
2292 % %
2293 % %
2294 + R e n d e r X 1 1 %
2295 % %
2296 % %
2297 % %
2298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2299 %
2300 % RenderX11() renders text on the image with an X11 font. It also returns the
2301 % bounding box of the text relative to the image.
2302 %
2303 % The format of the RenderX11 method is:
2304 %
2305 % MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
2306 % const PointInfo *offset,TypeMetric *metrics)
2307 %
2308 % A description of each parameter follows:
2309 %
2310 % o image: the image.
2311 %
2312 % o draw_info: the draw info.
2313 %
2314 % o offset: (x,y) location of text relative to image.
2315 %
2316 % o metrics: bounding box of text.
2317 %
2318 */
2319 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
2320  const PointInfo *offset,TypeMetric *metrics)
2321 {
2322  MagickBooleanType
2323  status;
2324 
2325  if (annotate_semaphore == (SemaphoreInfo *) NULL)
2326  ActivateSemaphoreInfo(&annotate_semaphore);
2327  LockSemaphoreInfo(annotate_semaphore);
2328  status=XRenderImage(image,draw_info,offset,metrics);
2329  UnlockSemaphoreInfo(annotate_semaphore);
2330  return(status);
2331 }
Definition: image.h:152
Definition: type.h:50