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