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