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