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