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