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