00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045 #include "magick/studio.h"
00046 #include "magick/annotate.h"
00047 #include "magick/cache-view.h"
00048 #include "magick/client.h"
00049 #include "magick/color.h"
00050 #include "magick/color-private.h"
00051 #include "magick/composite.h"
00052 #include "magick/composite-private.h"
00053 #include "magick/constitute.h"
00054 #include "magick/draw.h"
00055 #include "magick/draw-private.h"
00056 #include "magick/exception.h"
00057 #include "magick/exception-private.h"
00058 #include "magick/gem.h"
00059 #include "magick/geometry.h"
00060 #include "magick/image-private.h"
00061 #include "magick/log.h"
00062 #include "magick/quantum.h"
00063 #include "magick/quantum-private.h"
00064 #include "magick/property.h"
00065 #include "magick/resource_.h"
00066 #include "magick/statistic.h"
00067 #include "magick/string_.h"
00068 #include "magick/transform.h"
00069 #include "magick/type.h"
00070 #include "magick/utility.h"
00071 #include "magick/xwindow-private.h"
00072 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
00073 #if defined(__MINGW32__)
00074 # undef interface
00075 #endif
00076 #if defined(MAGICKCORE_HAVE_FT2BUILD_H)
00077 # include <ft2build.h>
00078 #endif
00079 #if defined(FT_FREETYPE_H)
00080 # include FT_FREETYPE_H
00081 #else
00082 # include <freetype/freetype.h>
00083 #endif
00084 #if defined(FT_GLYPH_H)
00085 # include FT_GLYPH_H
00086 #else
00087 # include <freetype/ftglyph.h>
00088 #endif
00089 #if defined(FT_OUTLINE_H)
00090 # include FT_OUTLINE_H
00091 #else
00092 # include <freetype/ftoutln.h>
00093 #endif
00094 #if defined(FT_BBOX_H)
00095 # include FT_BBOX_H
00096 #else
00097 # include <freetype/ftbbox.h>
00098 #endif
00099 #endif
00100
00101
00102
00103
00104 static MagickBooleanType
00105 RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
00106 RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
00107 RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
00108 TypeMetric *),
00109 RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *);
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159 MagickExport MagickBooleanType AnnotateImage(Image *image,
00160 const DrawInfo *draw_info)
00161 {
00162 char
00163 primitive[MaxTextExtent],
00164 **textlist;
00165
00166 DrawInfo
00167 *annotate,
00168 *annotate_info;
00169
00170 GeometryInfo
00171 geometry_info;
00172
00173 MagickBooleanType
00174 status;
00175
00176 PointInfo
00177 offset;
00178
00179 RectangleInfo
00180 geometry;
00181
00182 register long
00183 i;
00184
00185 size_t
00186 length;
00187
00188 TypeMetric
00189 metrics;
00190
00191 unsigned long
00192 height,
00193 number_lines;
00194
00195 assert(image != (Image *) NULL);
00196 assert(image->signature == MagickSignature);
00197 if (image->debug != MagickFalse)
00198 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00199 assert(draw_info != (DrawInfo *) NULL);
00200 assert(draw_info->signature == MagickSignature);
00201 if (draw_info->text == (char *) NULL)
00202 return(MagickFalse);
00203 if (*draw_info->text == '\0')
00204 return(MagickTrue);
00205 textlist=StringToList(draw_info->text);
00206 if (textlist == (char **) NULL)
00207 return(MagickFalse);
00208 length=strlen(textlist[0]);
00209 for (i=1; textlist[i] != (char *) NULL; i++)
00210 if (strlen(textlist[i]) > length)
00211 length=strlen(textlist[i]);
00212 number_lines=(unsigned long) i;
00213 annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00214 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00215 SetGeometry(image,&geometry);
00216 SetGeometryInfo(&geometry_info);
00217 if (annotate_info->geometry != (char *) NULL)
00218 {
00219 (void) ParsePageGeometry(image,annotate_info->geometry,&geometry);
00220 (void) ParseGeometry(annotate_info->geometry,&geometry_info);
00221 }
00222 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00223 return(MagickFalse);
00224 status=MagickTrue;
00225 for (i=0; textlist[i] != (char *) NULL; i++)
00226 {
00227
00228
00229
00230 annotate_info->affine.tx=geometry_info.xi-image->page.x;
00231 annotate_info->affine.ty=geometry_info.psi-image->page.y;
00232 (void) CloneString(&annotate->text,textlist[i]);
00233 (void) GetTypeMetrics(image,annotate,&metrics);
00234 height=(unsigned long) (metrics.ascent-metrics.descent+0.5);
00235 switch (annotate->gravity)
00236 {
00237 case UndefinedGravity:
00238 default:
00239 {
00240 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
00241 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
00242 break;
00243 }
00244 case NorthWestGravity:
00245 {
00246 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
00247 annotate_info->affine.ry*height+annotate_info->affine.ry*
00248 (metrics.ascent+metrics.descent);
00249 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
00250 annotate_info->affine.sy*height+annotate_info->affine.sy*
00251 metrics.ascent;
00252 break;
00253 }
00254 case NorthGravity:
00255 {
00256 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
00257 geometry.width/2.0+i*annotate_info->affine.ry*height-
00258 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
00259 annotate_info->affine.ry*(metrics.ascent+metrics.descent);
00260 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
00261 annotate_info->affine.sy*height+annotate_info->affine.sy*
00262 metrics.ascent-annotate_info->affine.rx*(metrics.width-
00263 metrics.bounds.x1)/2.0;
00264 break;
00265 }
00266 case NorthEastGravity:
00267 {
00268 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
00269 geometry.width+i*annotate_info->affine.ry*height-
00270 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
00271 annotate_info->affine.ry*(metrics.ascent+metrics.descent);
00272 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
00273 annotate_info->affine.sy*height+annotate_info->affine.sy*
00274 metrics.ascent-annotate_info->affine.rx*(metrics.width-
00275 metrics.bounds.x1);
00276 break;
00277 }
00278 case WestGravity:
00279 {
00280 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
00281 annotate_info->affine.ry*height+annotate_info->affine.ry*
00282 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
00283 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
00284 geometry.height/2.0+i*annotate_info->affine.sy*height+
00285 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
00286 (number_lines-1.0)*height)/2.0;
00287 break;
00288 }
00289 case StaticGravity:
00290 case CenterGravity:
00291 {
00292 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
00293 geometry.width/2.0+i*annotate_info->affine.ry*height-
00294 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
00295 annotate_info->affine.ry*(metrics.ascent+metrics.descent-
00296 (number_lines-1)*height)/2.0;
00297 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
00298 geometry.height/2.0+i*annotate_info->affine.sy*height-
00299 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0+
00300 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
00301 (number_lines-1.0)*height)/2.0;
00302 break;
00303 }
00304 case EastGravity:
00305 {
00306 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
00307 geometry.width+i*annotate_info->affine.ry*height-
00308 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
00309 annotate_info->affine.ry*(metrics.ascent+metrics.descent-
00310 (number_lines-1.0)*height)/2.0;
00311 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
00312 geometry.height/2.0+i*annotate_info->affine.sy*height-
00313 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)+
00314 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
00315 (number_lines-1.0)*height)/2.0;
00316 break;
00317 }
00318 case SouthWestGravity:
00319 {
00320 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
00321 annotate_info->affine.ry*height-annotate_info->affine.ry*
00322 (number_lines-1.0)*height;
00323 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
00324 geometry.height+i*annotate_info->affine.sy*height-
00325 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
00326 break;
00327 }
00328 case SouthGravity:
00329 {
00330 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
00331 geometry.width/2.0+i*annotate_info->affine.ry*height-
00332 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0-
00333 annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
00334 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
00335 geometry.height+i*annotate_info->affine.sy*height-
00336 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0-
00337 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
00338 break;
00339 }
00340 case SouthEastGravity:
00341 {
00342 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
00343 geometry.width+i*annotate_info->affine.ry*height-
00344 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)-
00345 annotate_info->affine.ry*(number_lines-1.0)*height;
00346 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
00347 geometry.height+i*annotate_info->affine.sy*height-
00348 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)-
00349 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
00350 break;
00351 }
00352 }
00353 switch (annotate->align)
00354 {
00355 case LeftAlign:
00356 {
00357 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
00358 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
00359 break;
00360 }
00361 case CenterAlign:
00362 {
00363 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
00364 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0;
00365 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
00366 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0;
00367 break;
00368 }
00369 case RightAlign:
00370 {
00371 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
00372 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1);
00373 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
00374 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1);
00375 break;
00376 }
00377 default:
00378 break;
00379 }
00380 if (draw_info->undercolor.opacity != TransparentOpacity)
00381 {
00382 DrawInfo
00383 *undercolor_info;
00384
00385
00386
00387
00388 undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
00389 undercolor_info->fill=draw_info->undercolor;
00390 undercolor_info->affine=draw_info->affine;
00391 undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
00392 undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
00393 (void) FormatMagickString(primitive,MaxTextExtent,
00394 "rectangle 0,0 %g,%ld",metrics.origin.x,height);
00395 (void) CloneString(&undercolor_info->primitive,primitive);
00396 (void) DrawImage(image,undercolor_info);
00397 (void) DestroyDrawInfo(undercolor_info);
00398 }
00399 annotate_info->affine.tx=offset.x;
00400 annotate_info->affine.ty=offset.y;
00401 (void) FormatMagickString(primitive,MaxTextExtent,"stroke-width %g "
00402 "line 0,0 %g,0",metrics.underline_thickness,metrics.width);
00403 if (annotate->decorate == OverlineDecoration)
00404 {
00405 annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
00406 metrics.descent-metrics.underline_position));
00407 (void) CloneString(&annotate_info->primitive,primitive);
00408 (void) DrawImage(image,annotate_info);
00409 }
00410 else
00411 if (annotate->decorate == UnderlineDecoration)
00412 {
00413 annotate_info->affine.ty-=(draw_info->affine.sy*
00414 metrics.underline_position);
00415 (void) CloneString(&annotate_info->primitive,primitive);
00416 (void) DrawImage(image,annotate_info);
00417 }
00418
00419
00420
00421 status=RenderType(image,annotate,&offset,&metrics);
00422 if (status == MagickFalse)
00423 break;
00424 if (annotate->decorate == LineThroughDecoration)
00425 {
00426 annotate_info->affine.ty-=(draw_info->affine.sy*(height+
00427 metrics.underline_position+metrics.descent)/2.0);
00428 (void) CloneString(&annotate_info->primitive,primitive);
00429 (void) DrawImage(image,annotate_info);
00430 }
00431 }
00432
00433
00434
00435 annotate_info=DestroyDrawInfo(annotate_info);
00436 annotate=DestroyDrawInfo(annotate);
00437 for (i=0; textlist[i] != (char *) NULL; i++)
00438 textlist[i]=DestroyString(textlist[i]);
00439 textlist=(char **) RelinquishMagickMemory(textlist);
00440 return(status);
00441 }
00442
00443 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476 static int GetOneCharacter(const unsigned char *text,size_t *length)
00477 {
00478 int
00479 c;
00480
00481 if (*length < 1)
00482 return(-1);
00483 c=text[0];
00484 if ((c & 0x80) == 0)
00485 {
00486 *length=1;
00487 return((int) c);
00488 }
00489 if (*length < 2)
00490 {
00491 *length=0;
00492 return(-1);
00493 }
00494 *length=2;
00495 c=((int) (text[0]) << 8);
00496 c|=text[1];
00497 return((int) c);
00498 }
00499
00500 static unsigned long *EncodeSJIS(const char *text,size_t *count)
00501 {
00502 int
00503 c;
00504
00505 register const char
00506 *p;
00507
00508 register unsigned long
00509 *q;
00510
00511 size_t
00512 length;
00513
00514 unsigned long
00515 *encoding;
00516
00517 *count=0;
00518 if ((text == (char *) NULL) || (*text == '\0'))
00519 return((unsigned long *) NULL);
00520 length=strlen(text);
00521 if (~length < MaxTextExtent)
00522 return((unsigned long *) NULL);
00523 length+=MaxTextExtent;
00524 encoding=(unsigned long *) AcquireQuantumMemory(length,sizeof(*encoding));
00525 if (encoding == (unsigned long *) NULL)
00526 return((unsigned long *) NULL);
00527 q=encoding;
00528 for (p=text; *p != '\0'; p+=length)
00529 {
00530 length=strlen(p);
00531 c=GetOneCharacter((const unsigned char *) p,&length);
00532 if (c < 0)
00533 {
00534 q=encoding;
00535 for (p=text; *p != '\0'; p++)
00536 *q++=(unsigned char) *p;
00537 break;
00538 }
00539 *q=(unsigned long) c;
00540 q++;
00541 }
00542 *count=q-encoding;
00543 return(encoding);
00544 }
00545 #endif
00546
00547 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573 static unsigned long *EncodeText(const char *text,size_t *count)
00574 {
00575 register const char
00576 *p;
00577
00578 register unsigned long
00579 *q;
00580
00581 size_t
00582 length;
00583
00584 unsigned long
00585 *encoding;
00586
00587 *count=0;
00588 if ((text == (char *) NULL) || (*text == '\0'))
00589 return((unsigned long *) NULL);
00590 length=strlen(text);
00591 if (~length < MaxTextExtent)
00592 return((unsigned long *) NULL);
00593 length+=MaxTextExtent;
00594 encoding=(unsigned long *) AcquireQuantumMemory(length,sizeof(*encoding));
00595 if (encoding == (unsigned long *) NULL)
00596 return((unsigned long *) NULL);
00597 q=encoding;
00598 for (p=text; *p != '\0'; p++)
00599 *q++=(unsigned char) *p;
00600 *count=q-encoding;
00601 return(encoding);
00602 }
00603 #endif
00604
00605 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636 static long GetUnicodeCharacter(const unsigned char *text,size_t *length)
00637 {
00638 unsigned long
00639 c;
00640
00641 if (*length < 1)
00642 return(-1);
00643 c=text[0];
00644 if ((c & 0x80) == 0)
00645 {
00646 *length=1;
00647 return((long) c);
00648 }
00649 if ((*length < 2) || ((text[1] & 0xc0) != 0x80))
00650 {
00651 *length=0;
00652 return(-1);
00653 }
00654 if ((c & 0xe0) != 0xe0)
00655 {
00656 *length=2;
00657 c=(text[0] & 0x1f) << 6;
00658 c|=text[1] & 0x3f;
00659 return((long) c);
00660 }
00661 if ((*length < 3) || ((text[2] & 0xc0) != 0x80))
00662 {
00663 *length=0;
00664 return(-1);
00665 }
00666 if ((c & 0xf0) != 0xf0)
00667 {
00668 *length=3;
00669 c=(text[0] & 0xf) << 12;
00670 c|=(text[1] & 0x3f) << 6;
00671 c|=text[2] & 0x3f;
00672 return((long) c);
00673 }
00674 if ((*length < 4) || ((c & 0xf8) != 0xf0) || ((text[3] & 0xc0) != 0x80))
00675 {
00676 *length=0;
00677 return(-1);
00678 }
00679 *length=4;
00680 c=(text[0] & 0x7) << 18;
00681 c|=(text[1] & 0x3f) << 12;
00682 c|=(text[2] & 0x3f) << 6;
00683 c|=text[3] & 0x3f;
00684 return((long) c);
00685 }
00686
00687 static unsigned long *EncodeUnicode(const char *text,size_t *count)
00688 {
00689 long
00690 c;
00691
00692 register const char
00693 *p;
00694
00695 register unsigned long
00696 *q;
00697
00698 size_t
00699 length;
00700
00701 unsigned long
00702 *unicode;
00703
00704 *count=0;
00705 if ((text == (char *) NULL) || (*text == '\0'))
00706 return((unsigned long *) NULL);
00707 length=strlen(text);
00708 if (~length < MaxTextExtent)
00709 return((unsigned long *) NULL);
00710 length+=MaxTextExtent;
00711 unicode=(unsigned long *) AcquireQuantumMemory(length,sizeof(*unicode));
00712 if (unicode == (unsigned long *) NULL)
00713 return((unsigned long *) NULL);
00714 q=unicode;
00715 for (p=text; *p != '\0'; p+=length)
00716 {
00717 length=strlen(p);
00718 c=GetUnicodeCharacter((const unsigned char *) p,&length);
00719 if (c < 0)
00720 {
00721 q=unicode;
00722 for (p=text; *p != '\0'; p++)
00723 *q++=(unsigned char) *p;
00724 break;
00725 }
00726 *q=(unsigned long) c;
00727 q++;
00728 }
00729 *count=q-unicode;
00730 return(unicode);
00731 }
00732 #endif
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762 MagickExport long FormatMagickCaption(Image *image,DrawInfo *draw_info,
00763 char *caption,TypeMetric *metrics)
00764 {
00765 MagickBooleanType
00766 status;
00767
00768 register char
00769 *p,
00770 *q,
00771 *s;
00772
00773 register long
00774 i;
00775
00776 unsigned long
00777 width;
00778
00779 q=draw_info->text;
00780 s=(char *) NULL;
00781 for (p=caption; *p != '\0'; p++)
00782 {
00783 if (isspace((int) ((unsigned char) *p)) != 0)
00784 s=p;
00785 *q++=(*p);
00786 *q='\0';
00787 status=GetTypeMetrics(image,draw_info,metrics);
00788 if (status == MagickFalse)
00789 break;
00790 width=(unsigned long) (metrics->width+0.5);
00791 if (*p != '\n')
00792 if (width <= image->columns)
00793 continue;
00794 if (s == (char *) NULL)
00795 {
00796 s=p;
00797 while ((isspace((int) ((unsigned char) *s)) == 0) && (*s != '\0'))
00798 s++;
00799 }
00800 if (*s != '\0')
00801 {
00802 *s='\n';
00803 p=s;
00804 s=(char *) NULL;
00805 }
00806 q=draw_info->text;
00807 }
00808 i=0;
00809 for (p=caption; *p != '\0'; p++)
00810 if (*p == '\n')
00811 i++;
00812 return(i);
00813 }
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
00863 const DrawInfo *draw_info,TypeMetric *metrics)
00864 {
00865 char
00866 **textlist;
00867
00868 DrawInfo
00869 *annotate_info;
00870
00871 MagickBooleanType
00872 status;
00873
00874 register long
00875 i;
00876
00877 TypeMetric
00878 extent;
00879
00880 unsigned long
00881 number_lines;
00882
00883 assert(image != (Image *) NULL);
00884 assert(image->signature == MagickSignature);
00885 if (image->debug != MagickFalse)
00886 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00887 assert(draw_info != (DrawInfo *) NULL);
00888 assert(draw_info->text != (char *) NULL);
00889 assert(draw_info->signature == MagickSignature);
00890 if (*draw_info->text == '\0')
00891 return(MagickFalse);
00892 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00893 annotate_info->text=DestroyString(annotate_info->text);
00894
00895
00896
00897 textlist=StringToList(draw_info->text);
00898 if (textlist == (char **) NULL)
00899 return(MagickFalse);
00900 annotate_info->render=MagickFalse;
00901 (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
00902 (void) ResetMagickMemory(&extent,0,sizeof(extent));
00903
00904
00905
00906 annotate_info->text=textlist[0];
00907 status=GetTypeMetrics(image,annotate_info,&extent);
00908 *metrics=extent;
00909 for (i=1; textlist[i] != (char *) NULL; i++)
00910 {
00911 annotate_info->text=textlist[i];
00912 status=GetTypeMetrics(image,annotate_info,&extent);
00913 if (extent.width > metrics->width)
00914 *metrics=extent;
00915 }
00916 number_lines=(unsigned long) i;
00917 metrics->height=(double) number_lines*(long) (metrics->ascent-
00918 metrics->descent+0.5);
00919
00920
00921
00922 annotate_info->text=(char *) NULL;
00923 annotate_info=DestroyDrawInfo(annotate_info);
00924 for (i=0; textlist[i] != (char *) NULL; i++)
00925 textlist[i]=DestroyString(textlist[i]);
00926 textlist=(char **) RelinquishMagickMemory(textlist);
00927 return(status);
00928 }
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
00975 const DrawInfo *draw_info,TypeMetric *metrics)
00976 {
00977 DrawInfo
00978 *annotate_info;
00979
00980 MagickBooleanType
00981 status;
00982
00983 PointInfo
00984 offset;
00985
00986 assert(image != (Image *) NULL);