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