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