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