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