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/attribute.h"
00048 #include "magick/cache-view.h"
00049 #include "magick/client.h"
00050 #include "magick/color.h"
00051 #include "magick/color-private.h"
00052 #include "magick/composite.h"
00053 #include "magick/composite-private.h"
00054 #include "magick/constitute.h"
00055 #include "magick/draw.h"
00056 #include "magick/draw-private.h"
00057 #include "magick/exception.h"
00058 #include "magick/exception-private.h"
00059 #include "magick/gem.h"
00060 #include "magick/geometry.h"
00061 #include "magick/image-private.h"
00062 #include "magick/log.h"
00063 #include "magick/quantum.h"
00064 #include "magick/quantum-private.h"
00065 #include "magick/property.h"
00066 #include "magick/resource_.h"
00067 #include "magick/statistic.h"
00068 #include "magick/string_.h"
00069 #include "magick/token-private.h"
00070 #include "magick/transform.h"
00071 #include "magick/type.h"
00072 #include "magick/utility.h"
00073 #include "magick/xwindow-private.h"
00074 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
00075 #if defined(__MINGW32__)
00076 # undef interface
00077 #endif
00078 #if defined(MAGICKCORE_HAVE_FT2BUILD_H)
00079 # include <ft2build.h>
00080 #endif
00081 #if defined(FT_FREETYPE_H)
00082 # include FT_FREETYPE_H
00083 #else
00084 # include <freetype/freetype.h>
00085 #endif
00086 #if defined(FT_GLYPH_H)
00087 # include FT_GLYPH_H
00088 #else
00089 # include <freetype/ftglyph.h>
00090 #endif
00091 #if defined(FT_OUTLINE_H)
00092 # include FT_OUTLINE_H
00093 #else
00094 # include <freetype/ftoutln.h>
00095 #endif
00096 #if defined(FT_BBOX_H)
00097 # include FT_BBOX_H
00098 #else
00099 # include <freetype/ftbbox.h>
00100 #endif
00101 #endif
00102
00103
00104
00105
00106 static MagickBooleanType
00107 RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
00108 RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
00109 RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
00110 TypeMetric *),
00111 RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *);
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
00160
00161 MagickExport MagickBooleanType AnnotateImage(Image *image,
00162 const DrawInfo *draw_info)
00163 {
00164 char
00165 primitive[MaxTextExtent],
00166 **textlist;
00167
00168 DrawInfo
00169 *annotate,
00170 *annotate_info;
00171
00172 GeometryInfo
00173 geometry_info;
00174
00175 MagickBooleanType
00176 status;
00177
00178 PointInfo
00179 offset;
00180
00181 RectangleInfo
00182 geometry;
00183
00184 register long
00185 i;
00186
00187 size_t
00188 length;
00189
00190 TypeMetric
00191 metrics;
00192
00193 unsigned long
00194 height,
00195 number_lines;
00196
00197 assert(image != (Image *) NULL);
00198 assert(image->signature == MagickSignature);
00199 if (image->debug != MagickFalse)
00200 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00201 assert(draw_info != (DrawInfo *) NULL);
00202 assert(draw_info->signature == MagickSignature);
00203 if (draw_info->text == (char *) NULL)
00204 return(MagickFalse);
00205 if (*draw_info->text == '\0')
00206 return(MagickTrue);
00207 textlist=StringToList(draw_info->text);
00208 if (textlist == (char **) NULL)
00209 return(MagickFalse);
00210 length=strlen(textlist[0]);
00211 for (i=1; textlist[i] != (char *) NULL; i++)
00212 if (strlen(textlist[i]) > length)
00213 length=strlen(textlist[i]);
00214 number_lines=(unsigned long) i;
00215 annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00216 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00217 SetGeometry(image,&geometry);
00218 SetGeometryInfo(&geometry_info);
00219 if (annotate_info->geometry != (char *) NULL)
00220 {
00221 (void) ParsePageGeometry(image,annotate_info->geometry,&geometry,
00222 &image->exception);
00223 (void) ParseGeometry(annotate_info->geometry,&geometry_info);
00224 }
00225 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00226 return(MagickFalse);
00227 status=MagickTrue;
00228 for (i=0; textlist[i] != (char *) NULL; i++)
00229 {
00230
00231
00232
00233 annotate_info->affine.tx=geometry_info.xi-image->page.x;
00234 annotate_info->affine.ty=geometry_info.psi-image->page.y;
00235 (void) CloneString(&annotate->text,textlist[i]);
00236 (void) GetTypeMetrics(image,annotate,&metrics);
00237 height=(long) (metrics.ascent-metrics.descent+
00238 draw_info->interline_spacing+0.5);
00239 switch (annotate->gravity)
00240 {
00241 case UndefinedGravity:
00242 default:
00243 {
00244 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
00245 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
00246 break;
00247 }
00248 case NorthWestGravity:
00249 {
00250 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
00251 annotate_info->affine.ry*height+annotate_info->affine.ry*
00252 (metrics.ascent+metrics.descent);
00253 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
00254 annotate_info->affine.sy*height+annotate_info->affine.sy*
00255 metrics.ascent;
00256 break;
00257 }
00258 case NorthGravity:
00259 {
00260 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
00261 geometry.width/2.0+i*annotate_info->affine.ry*height-
00262 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
00263 annotate_info->affine.ry*(metrics.ascent+metrics.descent);
00264 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
00265 annotate_info->affine.sy*height+annotate_info->affine.sy*
00266 metrics.ascent-annotate_info->affine.rx*(metrics.width-
00267 metrics.bounds.x1)/2.0;
00268 break;
00269 }
00270 case NorthEastGravity:
00271 {
00272 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
00273 geometry.width+i*annotate_info->affine.ry*height-
00274 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
00275 annotate_info->affine.ry*(metrics.ascent+metrics.descent)-1.0;
00276 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
00277 annotate_info->affine.sy*height+annotate_info->affine.sy*
00278 metrics.ascent-annotate_info->affine.rx*(metrics.width-
00279 metrics.bounds.x1);
00280 break;
00281 }
00282 case WestGravity:
00283 {
00284 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
00285 annotate_info->affine.ry*height+annotate_info->affine.ry*
00286 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
00287 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
00288 geometry.height/2.0+i*annotate_info->affine.sy*height+
00289 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
00290 (number_lines-1.0)*height)/2.0;
00291 break;
00292 }
00293 case StaticGravity:
00294 case CenterGravity:
00295 {
00296 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
00297 geometry.width/2.0+i*annotate_info->affine.ry*height-
00298 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
00299 annotate_info->affine.ry*(metrics.ascent+metrics.descent-
00300 (number_lines-1)*height)/2.0;
00301 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
00302 geometry.height/2.0+i*annotate_info->affine.sy*height-
00303 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0+
00304 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
00305 (number_lines-1.0)*height)/2.0;
00306 break;
00307 }
00308 case EastGravity:
00309 {
00310 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
00311 geometry.width+i*annotate_info->affine.ry*height-
00312 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
00313 annotate_info->affine.ry*(metrics.ascent+metrics.descent-
00314 (number_lines-1.0)*height)/2.0-1.0;
00315 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
00316 geometry.height/2.0+i*annotate_info->affine.sy*height-
00317 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)+
00318 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
00319 (number_lines-1.0)*height)/2.0;
00320 break;
00321 }
00322 case SouthWestGravity:
00323 {
00324 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
00325 annotate_info->affine.ry*height-annotate_info->affine.ry*
00326 (number_lines-1.0)*height;
00327 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
00328 geometry.height+i*annotate_info->affine.sy*height-
00329 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
00330 break;
00331 }
00332 case SouthGravity:
00333 {
00334 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
00335 geometry.width/2.0+i*annotate_info->affine.ry*height-
00336 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0-
00337 annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
00338 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
00339 geometry.height+i*annotate_info->affine.sy*height-
00340 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0-
00341 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
00342 break;
00343 }
00344 case SouthEastGravity:
00345 {
00346 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
00347 geometry.width+i*annotate_info->affine.ry*height-
00348 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)-
00349 annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
00350 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
00351 geometry.height+i*annotate_info->affine.sy*height-
00352 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)-
00353 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
00354 break;
00355 }
00356 }
00357 switch (annotate->align)
00358 {
00359 case LeftAlign:
00360 {
00361 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
00362 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
00363 break;
00364 }
00365 case CenterAlign:
00366 {
00367 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
00368 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0;
00369 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
00370 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0;
00371 break;
00372 }
00373 case RightAlign:
00374 {
00375 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
00376 annotate_info->affine.sx*(metrics.width+metrics.bounds.x1);
00377 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
00378 annotate_info->affine.rx*(metrics.width+metrics.bounds.x1);
00379 break;
00380 }
00381 default:
00382 break;
00383 }
00384 if (draw_info->undercolor.opacity != TransparentOpacity)
00385 {
00386 DrawInfo
00387 *undercolor_info;
00388
00389
00390
00391
00392 undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
00393 undercolor_info->fill=draw_info->undercolor;
00394 undercolor_info->affine=draw_info->affine;
00395 undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
00396 undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
00397 (void) FormatMagickString(primitive,MaxTextExtent,
00398 "rectangle 0,0 %g,%lu",metrics.origin.x,height);
00399 (void) CloneString(&undercolor_info->primitive,primitive);
00400 (void) DrawImage(image,undercolor_info);
00401 (void) DestroyDrawInfo(undercolor_info);
00402 }
00403 annotate_info->affine.tx=offset.x;
00404 annotate_info->affine.ty=offset.y;
00405 (void) FormatMagickString(primitive,MaxTextExtent,"stroke-width %g "
00406 "line 0,0 %g,0",metrics.underline_thickness,metrics.width);
00407 if (annotate->decorate == OverlineDecoration)
00408 {
00409 annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
00410 metrics.descent-metrics.underline_position));
00411 (void) CloneString(&annotate_info->primitive,primitive);
00412 (void) DrawImage(image,annotate_info);
00413 }
00414 else
00415 if (annotate->decorate == UnderlineDecoration)
00416 {
00417 annotate_info->affine.ty-=(draw_info->affine.sy*
00418 metrics.underline_position);
00419 (void) CloneString(&annotate_info->primitive,primitive);
00420 (void) DrawImage(image,annotate_info);
00421 }
00422
00423
00424
00425 status=RenderType(image,annotate,&offset,&metrics);
00426 if (status == MagickFalse)
00427 break;
00428 if (annotate->decorate == LineThroughDecoration)
00429 {
00430 annotate_info->affine.ty-=(draw_info->affine.sy*(height+
00431 metrics.underline_position+metrics.descent)/2.0);
00432 (void) CloneString(&annotate_info->primitive,primitive);
00433 (void) DrawImage(image,annotate_info);
00434 }
00435 }
00436
00437
00438
00439 annotate_info=DestroyDrawInfo(annotate_info);
00440 annotate=DestroyDrawInfo(annotate);
00441 for (i=0; textlist[i] != (char *) NULL; i++)
00442 textlist[i]=DestroyString(textlist[i]);
00443 textlist=(char **) RelinquishMagickMemory(textlist);
00444 return(status);
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
00477 MagickExport long FormatMagickCaption(Image *image,DrawInfo *draw_info,
00478 TypeMetric *metrics,char **caption)
00479 {
00480 MagickBooleanType
00481 status;
00482
00483 register char
00484 *p,
00485 *q,
00486 *s;
00487
00488 register long
00489 i;
00490
00491 unsigned long
00492 width;
00493
00494 q=draw_info->text;
00495 s=(char *) NULL;
00496 for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
00497 {
00498 if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
00499 s=p;
00500 for (i=0; i < (long) GetUTFOctets(p); i++)
00501 *q++=(*(p+i));
00502 *q='\0';
00503 status=GetTypeMetrics(image,draw_info,metrics);
00504 if (status == MagickFalse)
00505 break;
00506 width=(unsigned long) (metrics->width+0.5);
00507 if (GetUTFCode(p) != '\n')
00508 if (width <= image->columns)
00509 continue;
00510 if (s == (char *) NULL)
00511 {
00512 s=p;
00513 while ((IsUTFSpace(GetUTFCode(s)) == MagickFalse) &&
00514 (GetUTFCode(s) != 0))
00515 s+=GetUTFOctets(s);
00516 }
00517 if (GetUTFCode(s) != 0)
00518 {
00519 *s='\n';
00520 p=s;
00521 }
00522 else
00523 {
00524 char
00525 *target;
00526
00527 long
00528 n;
00529
00530
00531
00532
00533 target=AcquireString(*caption);
00534 n=p-(*caption);
00535 CopyMagickString(target,*caption,n+1);
00536 ConcatenateMagickString(target,"\n",strlen(*caption)+1);
00537 ConcatenateMagickString(target,p,strlen(*caption)+2);
00538 (void) DestroyString(*caption);
00539 *caption=target;
00540 p=(*caption)+n;
00541 }
00542 s=(char *) NULL;
00543 q=draw_info->text;
00544 }
00545 i=0;
00546 for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
00547 if (GetUTFCode(p) == '\n')
00548 i++;
00549 return(i);
00550 }
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
00600 const DrawInfo *draw_info,TypeMetric *metrics)
00601 {
00602 char
00603 **textlist;
00604
00605 DrawInfo
00606 *annotate_info;
00607
00608 MagickBooleanType
00609 status;
00610
00611 register long
00612 i;
00613
00614 TypeMetric
00615 extent;
00616
00617 assert(image != (Image *) NULL);
00618 assert(image->signature == MagickSignature);
00619 if (image->debug != MagickFalse)
00620 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00621 assert(draw_info != (DrawInfo *) NULL);
00622 assert(draw_info->text != (char *) NULL);
00623 assert(draw_info->signature == MagickSignature);
00624 if (*draw_info->text == '\0')
00625 return(MagickFalse);
00626 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00627 annotate_info->text=DestroyString(annotate_info->text);
00628
00629
00630
00631 textlist=StringToList(draw_info->text);
00632 if (textlist == (char **) NULL)
00633 return(MagickFalse);
00634 annotate_info->render=MagickFalse;
00635 (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
00636 (void) ResetMagickMemory(&extent,0,sizeof(extent));
00637
00638
00639
00640 annotate_info->text=textlist[0];
00641 status=GetTypeMetrics(image,annotate_info,&extent);
00642 *metrics=extent;
00643 for (i=1; textlist[i] != (char *) NULL; i++)
00644 {
00645 annotate_info->text=textlist[i];
00646 status=GetTypeMetrics(image,annotate_info,&extent);
00647 if (extent.width > metrics->width)
00648 *metrics=extent;
00649 }
00650 metrics->height=(double) (i*(unsigned long) (metrics->ascent-
00651 metrics->descent+0.5)+(i-1)*draw_info->interline_spacing);
00652
00653
00654
00655 annotate_info->text=(char *) NULL;
00656 annotate_info=DestroyDrawInfo(annotate_info);
00657 for (i=0; textlist[i] != (char *) NULL; i++)
00658 textlist[i]=DestroyString(textlist[i]);
00659 textlist=(char **) RelinquishMagickMemory(textlist);
00660 return(status);
00661 }
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
00708 const DrawInfo *draw_info,TypeMetric *metrics)
00709 {
00710 DrawInfo
00711 *annotate_info;
00712
00713 MagickBooleanType
00714 status;
00715
00716 PointInfo
00717 offset;
00718
00719 assert(image != (Image *) NULL);
00720 assert(image->signature == MagickSignature);
00721 if (image->debug != MagickFalse)
00722 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00723 assert(draw_info != (DrawInfo *) NULL);
00724 assert(draw_info->text != (char *) NULL);
00725 assert(draw_info->signature == MagickSignature);
00726 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00727 annotate_info->render=MagickFalse;
00728 (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
00729 offset.x=0.0;
00730 offset.y=0.0;
00731 status=RenderType(image,annotate_info,&offset,metrics);
00732 if (image->debug != MagickFalse)
00733 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
00734 "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
00735 "bounds: %g,%g %g,%g; origin: %g,%g; pixels per em: %g,%g; "
00736 "underline position: %g; underline thickness: %g",annotate_info->text,
00737 metrics->width,metrics->height,metrics->ascent,metrics->descent,
00738 metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
00739 metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
00740 metrics->pixels_per_em.x,metrics->pixels_per_em.y,
00741 metrics->underline_position,metrics->underline_thickness);
00742 annotate_info=DestroyDrawInfo(annotate_info);
00743 return(status);
00744 }
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
00777 const PointInfo *offset,TypeMetric *metrics)
00778 {
00779 const TypeInfo
00780 *type_info;
00781
00782 DrawInfo
00783 *annotate_info;
00784
00785 MagickBooleanType
00786 status;
00787
00788 type_info=(const TypeInfo *) NULL;
00789 if (draw_info->font != (char *) NULL)
00790 {
00791 if (*draw_info->font == '@')
00792 {
00793 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
00794 metrics);
00795 return(status);
00796 }
00797 if (*draw_info->font == '-')
00798 return(RenderX11(image,draw_info,offset,metrics));
00799 if (IsPathAccessible(draw_info->font) != MagickFalse)
00800 {
00801 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
00802 metrics);
00803 return(status);
00804 }
00805 type_info=GetTypeInfo(draw_info->font,&image->exception);
00806 if (type_info == (const TypeInfo *) NULL)
00807 (void) ThrowMagickException(&image->exception,GetMagickModule(),
00808 TypeWarning,"UnableToReadFont","`%s'",draw_info->font);
00809 }
00810 if ((type_info == (const TypeInfo *) NULL) &&
00811 (draw_info->family != (const char *) NULL))
00812 {
00813 type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
00814 draw_info->stretch,draw_info->weight,&image->exception);
00815 if (type_info == (const TypeInfo *) NULL)
00816 (void) ThrowMagickException(&image->exception,GetMagickModule(),
00817 TypeWarning,"UnableToReadFont","`%s'",draw_info->family);
00818 }
00819 if (type_info == (const TypeInfo *) NULL)
00820 type_info=GetTypeInfoByFamily("arial",draw_info->style,
00821 draw_info->stretch,draw_info->weight,&image->exception);
00822 if (type_info == (const TypeInfo *) NULL)
00823 type_info=GetTypeInfoByFamily("helvetica",draw_info->style,
00824 draw_info->stretch,draw_info->weight,&image->exception);
00825 if (type_info == (const TypeInfo *) NULL)
00826 type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
00827 draw_info->stretch,draw_info->weight,&image->exception);
00828 if (type_info == (const TypeInfo *) NULL)
00829 {
00830 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics);
00831 return(status);
00832 }
00833 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
00834 annotate_info->face=type_info->face;
00835 if (type_info->metrics != (char *) NULL)
00836 (void) CloneString(&annotate_info->metrics,type_info->metrics);
00837 if (type_info->glyphs != (char *) NULL)
00838 (void) CloneString(&annotate_info->font,type_info->glyphs);
00839 status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics);
00840 annotate_info=DestroyDrawInfo(annotate_info);
00841 return(status);
00842 }
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
00878 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
00879 DrawInfo *draw_info)
00880 {
00881 AffineMatrix
00882 affine;
00883
00884 char
00885 path[MaxTextExtent];
00886
00887 affine=draw_info->affine;
00888 (void) FormatMagickString(path,MaxTextExtent,"C%g,%g %g,%g %g,%g",
00889 affine.tx+p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,
00890 affine.ty-q->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0);
00891 (void) ConcatenateString(&draw_info->primitive,path);
00892 return(0);
00893 }
00894
00895 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
00896 {
00897 AffineMatrix
00898 affine;
00899
00900 char
00901 path[MaxTextExtent];
00902
00903 affine=draw_info->affine;
00904 (void) FormatMagickString(path,MaxTextExtent,"L%g,%g",affine.tx+to->x/64.0,
00905 affine.ty-to->y/64.0);
00906 (void) ConcatenateString(&draw_info->primitive,path);
00907 return(0);
00908 }
00909
00910 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
00911 {
00912 AffineMatrix
00913 affine;
00914
00915 char
00916 path[MaxTextExtent];
00917
00918 affine=draw_info->affine;
00919 (void) FormatMagickString(path,MaxTextExtent,"M%g,%g",affine.tx+to->x/64.0,
00920 affine.ty-to->y/64.0);
00921 (void) ConcatenateString(&draw_info->primitive,path);
00922 return(0);
00923 }
00924
00925 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
00926 DrawInfo *draw_info)
00927 {
00928 AffineMatrix
00929 affine;
00930
00931 char
00932 path[MaxTextExtent];
00933
00934 affine=draw_info->affine;
00935 (void) FormatMagickString(path,MaxTextExtent,"Q%g,%g %g,%g",
00936 affine.tx+control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,
00937 affine.ty-to->y/64.0);
00938 (void) ConcatenateString(&draw_info->primitive,path);
00939 return(0);
00940 }
00941
00942 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
00943 const char *encoding,const PointInfo *offset,TypeMetric *metrics)
00944 {
00945 #if !defined(FT_OPEN_PATHNAME)
00946 #define FT_OPEN_PATHNAME ft_open_pathname
00947 #endif
00948
00949 typedef struct _GlyphInfo
00950 {
00951 FT_UInt
00952 id;
00953
00954 FT_Vector
00955 origin;
00956
00957 FT_Glyph
00958 image;
00959 } GlyphInfo;
00960
00961 const char
00962 *value;
00963
00964 DrawInfo
00965 *annotate_info;
00966
00967 FT_BBox
00968 bounds;
00969
00970 FT_BitmapGlyph
00971 bitmap;
00972
00973 FT_Encoding
00974 encoding_type;
00975
00976 FT_Error
00977 status;
00978
00979 FT_Face
00980 face;
00981
00982 FT_Int32
00983 flags;
00984
00985 FT_Library
00986 library;
00987
00988 FT_Matrix
00989 affine;
00990
00991 FT_Open_Args
00992 args;
00993
00994 FT_Vector
00995 origin;
00996
00997 GlyphInfo
00998 glyph,
00999 last_glyph;
01000
01001 long
01002 code,
01003 y;
01004
01005 PointInfo
01006 point,
01007 resolution;
01008
01009 register char
01010 *p;
01011
01012 static FT_Outline_Funcs
01013 OutlineMethods =
01014 {
01015 (FT_Outline_MoveTo_Func) TraceMoveTo,
01016 (FT_Outline_LineTo_Func) TraceLineTo,
01017 (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
01018 (FT_Outline_CubicTo_Func) TraceCubicBezier,
01019 0, 0
01020 };
01021
01022
01023
01024
01025 status=FT_Init_FreeType(&library);
01026 if (status != 0)
01027 ThrowBinaryException(TypeError,"UnableToInitializeFreetypeLibrary",
01028 image->filename);
01029 args.flags=FT_OPEN_PATHNAME;
01030 if (draw_info->font == (char *) NULL)
01031 args.pathname=ConstantString("helvetica");
01032 else
01033 if (*draw_info->font != '@')
01034 args.pathname=ConstantString(draw_info->font);
01035 else
01036 args.pathname=ConstantString(draw_info->font+1);
01037 face=(FT_Face) NULL;
01038 status=FT_Open_Face(library,&args,draw_info->face,&face);
01039 args.pathname=DestroyString(args.pathname);
01040 if (status != 0)
01041 {
01042 (void) FT_Done_FreeType(library);
01043 (void) ThrowMagickException(&image->exception,GetMagickModule(),
01044 TypeError,"UnableToReadFont","`%s'",draw_info->font);
01045 return(RenderPostscript(image,draw_info,offset,metrics));
01046 }
01047 if ((draw_info->metrics != (char *) NULL) &&
01048 (IsPathAccessible(draw_info->metrics) != MagickFalse))
01049 (void) FT_Attach_File(face,draw_info->metrics);
01050 encoding_type=ft_encoding_unicode;
01051 status=FT_Select_Charmap(face,encoding_type);
01052 if ((status != 0) && (face->num_charmaps != 0))
01053 status=FT_Set_Charmap(face,face->charmaps[0]);
01054 if (encoding != (const char *) NULL)
01055 {
01056 if (LocaleCompare(encoding,"AdobeCustom") == 0)
01057 encoding_type=ft_encoding_adobe_custom;
01058 if (LocaleCompare(encoding,"AdobeExpert") == 0)
01059 encoding_type=ft_encoding_adobe_expert;
01060 if (LocaleCompare(encoding,"AdobeStandard") == 0)
01061 encoding_type=ft_encoding_adobe_standard;
01062 if (LocaleCompare(encoding,"AppleRoman") == 0)
01063 encoding_type=ft_encoding_apple_roman;
01064 if (LocaleCompare(encoding,"BIG5") == 0)
01065 encoding_type=ft_encoding_big5;
01066 if (LocaleCompare(encoding,"GB2312") == 0)
01067 encoding_type=ft_encoding_gb2312;
01068 if (LocaleCompare(encoding,"Johab") == 0)
01069 encoding_type=ft_encoding_johab;
01070 #if defined(ft_encoding_latin_1)
01071 if (LocaleCompare(encoding,"Latin-1") == 0)
01072 encoding_type=ft_encoding_latin_1;
01073 #endif
01074 if (LocaleCompare(encoding,"Latin-2") == 0)
01075 encoding_type=ft_encoding_latin_2;
01076 if (LocaleCompare(encoding,"None") == 0)
01077 encoding_type=ft_encoding_none;
01078 if (LocaleCompare(encoding,"SJIScode") == 0)
01079 encoding_type=ft_encoding_sjis;
01080 if (LocaleCompare(encoding,"Symbol") == 0)
01081 encoding_type=ft_encoding_symbol;
01082 if (LocaleCompare(encoding,"Unicode") == 0)
01083 encoding_type=ft_encoding_unicode;
01084 if (LocaleCompare(encoding,"Wansung") == 0)
01085 encoding_type=ft_encoding_wansung;
01086 status=FT_Select_Charmap(face,encoding_type);
01087 if (status != 0)
01088 ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
01089 }
01090
01091
01092
01093 resolution.x=DefaultResolution;
01094 resolution.y=DefaultResolution;
01095 if (draw_info->density != (char *) NULL)
01096 {
01097 GeometryInfo
01098 geometry_info;
01099
01100 MagickStatusType
01101 flags;
01102
01103 flags=ParseGeometry(draw_info->density,&geometry_info);
01104 resolution.x=geometry_info.rho;
01105 resolution.y=geometry_info.sigma;
01106 if ((flags & SigmaValue) == 0)
01107 resolution.y=resolution.x;
01108 }
01109 status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
01110 (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
01111 (FT_UInt) resolution.y);
01112 metrics->pixels_per_em.x=face->size->metrics.x_ppem;
01113 metrics->pixels_per_em.y=face->size->metrics.y_ppem;
01114 metrics->ascent=(double) face->size->metrics.ascender/64.0;
01115 metrics->descent=(double) face->size->metrics.descender/64.0;
01116 metrics->width=0;
01117 metrics->origin.x=0;
01118 metrics->origin.y=0;
01119 metrics->height=(double) face->size->metrics.height/64.0;
01120 metrics->max_advance=0.0;
01121 if (face->size->metrics.max_advance > MagickEpsilon)
01122 metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
01123 metrics->bounds.x1=0.0;
01124 metrics->bounds.y1=metrics->descent;
01125 metrics->bounds.x2=metrics->ascent+metrics->descent;
01126 metrics->bounds.y2=metrics->ascent+metrics->descent;
01127 metrics->underline_position=face->underline_position/64.0;
01128 metrics->underline_thickness=face->underline_thickness/64.0;
01129 if (*draw_info->text == '\0')
01130 {
01131 (void) FT_Done_Face(face);
01132 (void) FT_Done_FreeType(library);
01133 return(MagickTrue);
01134 }
01135
01136
01137
01138 if (image->debug != MagickFalse)
01139 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
01140 "font-encoding %s; text-encoding %s; pointsize %g",
01141 draw_info->font != (char *) NULL ? draw_info->font : "none",
01142 encoding != (char *) NULL ? encoding : "none",
01143 draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
01144 draw_info->pointsize);
01145 flags=FT_LOAD_NO_BITMAP;
01146 value=GetImageProperty(image,"type:hinting");
01147 if (LocaleCompare(value,"off") == 0)
01148 flags|=FT_LOAD_NO_HINTING;
01149 glyph.id=0;
01150 glyph.image=NULL;
01151 last_glyph.id=0;
01152 last_glyph.image=NULL;
01153 origin.x=0;
01154 origin.y=0;
01155 affine.xx=65536L;
01156 affine.yx=0L;
01157 affine.xy=0L;
01158 affine.yy=65536L;
01159 if (draw_info->render != MagickFalse)
01160 {
01161 affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
01162 affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
01163 affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
01164 affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
01165 }
01166 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
01167 (void) CloneString(&annotate_info->primitive,"path '");
01168 if (draw_info->render != MagickFalse)
01169 {
01170 if (image->storage_class != DirectClass)
01171 (void) SetImageStorageClass(image,DirectClass);
01172 if (image->matte == MagickFalse)
01173 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
01174 }
01175 point.x=0.0;
01176 point.y=0.0;
01177 code=0;
01178 for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
01179 {
01180 glyph.id=FT_Get_Char_Index(face,GetUTFCode(p));
01181 if (glyph.id == 0)
01182 glyph.id=FT_Get_Char_Index(face,'?');
01183 if ((glyph.id != 0) && (last_glyph.id != 0))
01184 {
01185 if (draw_info->kerning != 0.0)
01186 origin.x+=64.0*draw_info->kerning;
01187 else
01188 if (FT_HAS_KERNING(face))
01189 {
01190 FT_Vector
01191 kerning;
01192
01193 status=FT_Get_Kerning(face,last_glyph.id,glyph.id,
01194 ft_kerning_default,&kerning);
01195 if (status == 0)
01196 origin.x+=kerning.x;
01197 }
01198 }
01199 glyph.origin=origin;
01200 status=FT_Load_Glyph(face,glyph.id,flags);
01201 if (status != 0)
01202 continue;
01203 status=FT_Get_Glyph(face->glyph,&glyph.image);
01204 if (status != 0)
01205 continue;
01206 status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->outline,
01207 &bounds);
01208 if (status != 0)
01209 continue;
01210 if ((p == draw_info->text) || (bounds.xMin < metrics->bounds.x1))
01211 metrics->bounds.x1=bounds.xMin;
01212 if ((p == draw_info->text) || (bounds.yMin < metrics->bounds.y1))
01213 metrics->bounds.y1=bounds.yMin;
01214 if ((p == draw_info->text) || (bounds.xMax > metrics->bounds.x2))
01215 metrics->bounds.x2=bounds.xMax;
01216 if ((p == draw_info->text) || (bounds.yMax > metrics->bounds.y2))
01217 metrics->bounds.y2=bounds.yMax;
01218 if (draw_info->render != MagickFalse)
01219 if ((draw_info->stroke.opacity != TransparentOpacity) ||
01220 (draw_info->stroke_pattern != (Image *) NULL))
01221 {
01222
01223
01224
01225 annotate_info->affine.tx=glyph.origin.x/64.0;
01226 annotate_info->affine.ty=glyph.origin.y/64.0;
01227 (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->outline,
01228 &OutlineMethods,annotate_info);
01229 }
01230 FT_Vector_Transform(&glyph.origin,&affine);
01231 (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
01232 status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
01233 (FT_Vector *) NULL,MagickTrue);
01234 if (status != 0)
01235 continue;
01236 bitmap=(FT_BitmapGlyph) glyph.image;
01237 point.x=offset->x+bitmap->left;
01238 point.y=offset->y-bitmap->top;
01239 if (draw_info->render != MagickFalse)
01240 {
01241 CacheView
01242 *image_view;
01243
01244 ExceptionInfo
01245 *exception;
01246
01247 MagickBooleanType
01248 status;
01249
01250
01251
01252
01253 status=MagickTrue;
01254 exception=(&image->exception);
01255 image_view=AcquireCacheView(image);
01256 for (y=0; y < (long) bitmap->bitmap.rows; y++)
01257 {
01258 long
01259 x_offset,
01260 y_offset;
01261
01262 MagickBooleanType
01263 active,
01264 sync;
01265
01266 MagickRealType
01267 fill_opacity;
01268
01269 PixelPacket
01270 fill_color;
01271
01272 register long
01273 x;
01274
01275 register PixelPacket
01276 *__restrict q;
01277
01278 register unsigned char
01279 *p;
01280
01281 if (status == MagickFalse)
01282 continue;
01283 x_offset=(long) (point.x+0.5);
01284 y_offset=(long) (point.y+y+0.5);
01285 if ((y_offset < 0) || (y_offset >= (long) image->rows))
01286 continue;
01287 q=(PixelPacket *) NULL;
01288 if ((x_offset < 0) || (x_offset >= (long) image->columns))
01289 active=MagickFalse;
01290 else
01291 {
01292 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
01293 bitmap->bitmap.width,1,exception);
01294 active=q != (PixelPacket *) NULL ? MagickTrue : MagickFalse;
01295 }
01296 p=bitmap->bitmap.buffer+y*bitmap->bitmap.width;
01297 for (x=0; x < (long) bitmap->bitmap.width; x++)
01298 {
01299 x_offset++;
01300 if ((*p == 0) || (x_offset < 0) ||
01301 (x_offset >= (long) image->columns))
01302 {
01303 p++;
01304 q++;
01305 continue;
01306 }
01307 fill_opacity=(MagickRealType) (*p)/(bitmap->bitmap.num_grays-1);
01308 if (draw_info->text_antialias == MagickFalse)
01309 fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
01310 if (active == MagickFalse)
01311 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
01312 exception);
01313 if (q == (PixelPacket *) NULL)
01314 {
01315 p++;
01316 q++;
01317 continue;
01318 }
01319 (void) GetFillColor(draw_info,x_offset,y_offset,&fill_color);
01320 fill_opacity=QuantumRange-fill_opacity*(QuantumRange-
01321 fill_color.opacity);
01322 MagickCompositeOver(&fill_color,fill_opacity,q,q->opacity,q);
01323 if (active == MagickFalse)
01324 {
01325 sync=SyncCacheViewAuthenticPixels(image_view,exception);
01326 if (sync == MagickFalse)
01327 status=MagickFalse;
01328 }
01329 p++;
01330 q++;
01331 }
01332 sync=SyncCacheViewAuthenticPixels(image_view,exception);
01333 if (sync == MagickFalse)
01334 status=MagickFalse;
01335 }
01336 image_view=DestroyCacheView(image_view);
01337 }
01338 if ((bitmap->left+bitmap->bitmap.width) > metrics->width)
01339 metrics->width=bitmap->left+bitmap->bitmap.width;
01340 if ((draw_info->interword_spacing != 0.0) &&
01341 (IsUTFSpace(GetUTFCode(p)) != MagickFalse) &&
01342 (IsUTFSpace(code) == MagickFalse))
01343 origin.x+=64.0*draw_info->interword_spacing;
01344 else
01345 origin.x+=face->glyph->advance.x;
01346 metrics->origin.x=origin.x;
01347 metrics->origin.y=origin.y;
01348 if (last_glyph.id != 0)
01349 FT_Done_Glyph(last_glyph.image);
01350 last_glyph=glyph;
01351 code=GetUTFCode(p);
01352 }
01353 if (last_glyph.id != 0)
01354 FT_Done_Glyph(last_glyph.image);
01355 if ((draw_info->stroke.opacity != TransparentOpacity) ||
01356 (draw_info->stroke_pattern != (Image *) NULL))
01357 {
01358 if (draw_info->render != MagickFalse)
01359 {
01360
01361
01362
01363 annotate_info->linejoin=RoundJoin;
01364 annotate_info->affine.tx=offset->x;
01365 annotate_info->affine.ty=offset->y;
01366 (void) ConcatenateString(&annotate_info->primitive,"'");
01367 (void) DrawImage(image,annotate_info);
01368 }
01369 }
01370
01371
01372
01373 glyph.id=FT_Get_Char_Index(face,'_');
01374 glyph.origin=origin;
01375 status=FT_Load_Glyph(face,glyph.id,flags);
01376 if (status == 0)
01377 {
01378 status=FT_Get_Glyph(face->glyph,&glyph.image);
01379 if (status == 0)
01380 {
01381 status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->
01382 outline,&bounds);
01383 if (status == 0)
01384 {
01385 FT_Vector_Transform(&glyph.origin,&affine);
01386 (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
01387 status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
01388 (FT_Vector *) NULL,MagickTrue);
01389 bitmap=(FT_BitmapGlyph) glyph.image;
01390 if (bitmap->left > metrics->width)
01391 metrics->width=bitmap->left;
01392 }
01393 }
01394 if (glyph.id != 0)
01395 FT_Done_Glyph(glyph.image);
01396 }
01397 metrics->width-=metrics->bounds.x1/64.0;
01398 metrics->bounds.x1/=64.0;
01399 metrics->bounds.y1/=64.0;
01400 metrics->bounds.x2/=64.0;
01401 metrics->bounds.y2/=64.0;
01402 metrics->origin.x/=64.0;
01403 metrics->origin.y/=64.0;
01404
01405
01406
01407 annotate_info=DestroyDrawInfo(annotate_info);
01408 (void) FT_Done_Face(face);
01409 (void) FT_Done_FreeType(library);
01410 return(MagickTrue);
01411 }
01412 #else
01413 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
01414 const char *magick_unused(encoding),const PointInfo *offset,
01415 TypeMetric *metrics)
01416 {
01417 (void) ThrowMagickException(&image->exception,GetMagickModule(),
01418 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (Freetype)",
01419 draw_info->font);
01420 return(RenderPostscript(image,draw_info,offset,metrics));
01421 }
01422 #endif
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455 static inline size_t MagickMin(const size_t x,const size_t y)
01456 {
01457 if (x < y)
01458 return(x);
01459 return(y);
01460 }
01461
01462 static char *EscapeParenthesis(const char *text)
01463 {
01464 char
01465 *buffer;
01466
01467 register char
01468 *p;
01469
01470 register long
01471 i;
01472
01473 size_t
01474 escapes;
01475
01476 escapes=0;
01477 buffer=AcquireString(text);
01478 p=buffer;
01479 for (i=0; i < (long) MagickMin(strlen(text),MaxTextExtent-escapes-1); i++)
01480 {
01481 if ((text[i] == '(') || (text[i] == ')'))
01482 {
01483 *p++='\\';
01484 escapes++;
01485 }
01486 *p++=text[i];
01487 }
01488 *p='\0';
01489 return(buffer);
01490 }
01491
01492 static MagickBooleanType RenderPostscript(Image *image,
01493 const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics)
01494 {
01495 char
01496 filename[MaxTextExtent],
01497 geometry[MaxTextExtent],
01498 *text;
01499
01500 FILE
01501 *file;
01502
01503 Image
01504 *annotate_image;
01505
01506 ImageInfo
01507 *annotate_info;
01508
01509 int
01510 unique_file;
01511
01512 long
01513 y;
01514
01515 MagickBooleanType
01516 identity;
01517
01518 PointInfo
01519 extent,
01520 point,
01521 resolution;
01522
01523 register long
01524 i;
01525
01526
01527
01528
01529 if (image->debug != MagickFalse)
01530 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
01531 "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
01532 draw_info->font : "none",draw_info->pointsize);
01533 file=(FILE *) NULL;
01534 unique_file=AcquireUniqueFileResource(filename);
01535 if (unique_file != -1)
01536 file=fdopen(unique_file,"wb");
01537 if ((unique_file == -1) || (file == (FILE *) NULL))
01538 {
01539 ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
01540 filename);
01541 return(MagickFalse);
01542 }
01543 (void) fprintf(file,"%%!PS-Adobe-3.0\n");
01544 (void) fprintf(file,"/ReencodeType\n");
01545 (void) fprintf(file,"{\n");
01546 (void) fprintf(file," findfont dup length\n");
01547 (void) fprintf(file,
01548 " dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
01549 (void) fprintf(file,
01550 " /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
01551 (void) fprintf(file,"} bind def\n");
01552
01553
01554
01555 identity=(draw_info->affine.sx == draw_info->affine.sy) &&
01556 (draw_info->affine.rx == 0.0) && (draw_info->affine.ry == 0.0) ?
01557 MagickTrue : MagickFalse;
01558 extent.x=0.0;
01559 extent.y=0.0;
01560 for (i=0; i <= (long) (strlen(draw_info->text)+2); i++)
01561 {
01562 point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
01563 draw_info->affine.ry*2.0*draw_info->pointsize);
01564 point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
01565 draw_info->affine.sy*2.0*draw_info->pointsize);
01566 if (point.x > extent.x)
01567 extent.x=point.x;
01568 if (point.y > extent.y)
01569 extent.y=point.y;
01570 }
01571 (void) fprintf(file,"%g %g moveto\n",identity != MagickFalse ? 0.0 :
01572 extent.x/2.0,extent.y/2.0);
01573 (void) fprintf(file,"%g %g scale\n",draw_info->pointsize,
01574 draw_info->pointsize);
01575 if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
01576 (strchr(draw_info->font,'/') != (char *) NULL))
01577 (void) fprintf(file,
01578 "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
01579 else
01580 (void) fprintf(file,"/%s-ISO dup /%s ReencodeType findfont setfont\n",
01581 draw_info->font,draw_info->font);
01582 (void) fprintf(file,"[%g %g %g %g 0 0] concat\n",draw_info->affine.sx,
01583 -draw_info->affine.rx,-draw_info->affine.ry,draw_info->affine.sy);
01584 text=EscapeParenthesis(draw_info->text);
01585 if (identity == MagickFalse)
01586 (void) fprintf(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",text);
01587 (void) fprintf(file,"(%s) show\n",text);
01588 text=DestroyString(text);
01589 (void) fprintf(file,"showpage\n");
01590 (void) fclose(file);
01591 (void) FormatMagickString(geometry,MaxTextExtent,"%ldx%ld+0+0!",(long)
01592 (extent.x+0.5),(long) (extent.y+0.5));
01593 annotate_info=AcquireImageInfo();
01594 (void) FormatMagickString(annotate_info->filename,MaxTextExtent,"ps:%s",
01595 filename);
01596 (void) CloneString(&annotate_info->page,geometry);
01597 if (draw_info->density != (char *) NULL)
01598 (void) CloneString(&annotate_info->density,draw_info->density);
01599 annotate_info->antialias=draw_info->text_antialias;
01600 annotate_image=ReadImage(annotate_info,&image->exception);
01601 CatchException(&image->exception);
01602 annotate_info=DestroyImageInfo(annotate_info);
01603 (void) RelinquishUniqueFileResource(filename);
01604 if (annotate_image == (Image *) NULL)
01605 return(MagickFalse);
01606 resolution.x=DefaultResolution;
01607 resolution.y=DefaultResolution;
01608 if (draw_info->density != (char *) NULL)
01609 {
01610 GeometryInfo
01611 geometry_info;
01612
01613 MagickStatusType
01614 flags;
01615
01616 flags=ParseGeometry(draw_info->density,&geometry_info);
01617 resolution.x=geometry_info.rho;
01618 resolution.y=geometry_info.sigma;
01619 if ((flags & SigmaValue) == 0)
01620 resolution.y=resolution.x;
01621 }
01622 if (identity == MagickFalse)
01623 (void) TransformImage(&annotate_image,"0x0",(char *) NULL);
01624 else
01625 {
01626 RectangleInfo
01627 crop_info;
01628
01629 crop_info=GetImageBoundingBox(annotate_image,&annotate_image->exception);
01630 crop_info.height=(unsigned long) ((resolution.y/DefaultResolution)*
01631 ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
01632 crop_info.y=(long) ((resolution.y/DefaultResolution)*extent.y/8.0+0.5);
01633 (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu%+ld%+ld",
01634 crop_info.width,crop_info.height,crop_info.x,crop_info.y);
01635 (void) TransformImage(&annotate_image,geometry,(char *) NULL);
01636 }
01637 metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
01638 ExpandAffine(&draw_info->affine)*draw_info->pointsize;
01639 metrics->pixels_per_em.y=metrics->pixels_per_em.x;
01640 metrics->ascent=metrics->pixels_per_em.x;
01641 metrics->descent=metrics->pixels_per_em.y/-5.0;
01642 metrics->width=(double) annotate_image->columns/
01643 ExpandAffine(&draw_info->affine);
01644 metrics->height=1.152*metrics->pixels_per_em.x;
01645 metrics->max_advance=metrics->pixels_per_em.x;
01646 metrics->bounds.x1=0.0;
01647 metrics->bounds.y1=metrics->descent;
01648 metrics->bounds.x2=metrics->ascent+metrics->descent;
01649 metrics->bounds.y2=metrics->ascent+metrics->descent;
01650 metrics->underline_position=(-2.0);
01651 metrics->underline_thickness=1.0;
01652 if (draw_info->render == MagickFalse)
01653 {
01654 annotate_image=DestroyImage(annotate_image);
01655 return(MagickTrue);
01656 }
01657 if (draw_info->fill.opacity != TransparentOpacity)
01658 {
01659 ExceptionInfo
01660 *exception;
01661
01662 MagickBooleanType
01663 sync;
01664
01665 PixelPacket
01666 fill_color;
01667
01668 CacheView
01669 *annotate_view;
01670
01671
01672
01673
01674 if (image->matte == MagickFalse)
01675 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
01676 if (annotate_image->matte == MagickFalse)
01677 (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel);
01678 fill_color=draw_info->fill;
01679 exception=(&image->exception);
01680 annotate_view=AcquireCacheView(annotate_image);
01681 for (y=0; y < (long) annotate_image->rows; y++)
01682 {
01683 register long
01684 x;
01685
01686 register PixelPacket
01687 *__restrict q;
01688
01689 q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
01690 1,exception);
01691 if (q == (PixelPacket *) NULL)
01692 break;
01693 for (x=0; x < (long) annotate_image->columns; x++)
01694 {
01695 (void) GetFillColor(draw_info,x,y,&fill_color);
01696 q->opacity=RoundToQuantum(QuantumRange-(((QuantumRange-
01697 (MagickRealType) PixelIntensityToQuantum(q))*(QuantumRange-
01698 fill_color.opacity))/QuantumRange));
01699 q->red=fill_color.red;
01700 q->green=fill_color.green;
01701 q->blue=fill_color.blue;
01702 q++;
01703 }
01704 sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
01705 if (sync == MagickFalse)
01706 break;
01707 }
01708 annotate_view=DestroyCacheView(annotate_view);
01709 (void) CompositeImage(image,OverCompositeOp,annotate_image,
01710 (long) (offset->x+0.5),(long) (offset->y-(metrics->ascent+
01711 metrics->descent)+0.5));
01712 }
01713 annotate_image=DestroyImage(annotate_image);
01714 return(MagickTrue);
01715 }
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730
01731
01732
01733
01734
01735
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747 #if defined(MAGICKCORE_X11_DELEGATE)
01748 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
01749 const PointInfo *offset,TypeMetric *metrics)
01750 {
01751 MagickBooleanType
01752 status;
01753
01754 static DrawInfo
01755 cache_info;
01756
01757 static Display
01758 *display = (Display *) NULL;
01759
01760 static XAnnotateInfo
01761 annotate_info;
01762
01763 static XFontStruct
01764 *font_info;
01765
01766 static XPixelInfo
01767 pixel;
01768
01769 static XResourceInfo
01770 resource_info;
01771
01772 static XrmDatabase
01773 resource_database;
01774
01775 static XStandardColormap
01776 *map_info;
01777
01778 static XVisualInfo
01779 *visual_info;
01780
01781 unsigned long
01782 height,
01783 width;
01784
01785 if (display == (Display *) NULL)
01786 {
01787 const char
01788 *client_name;
01789
01790 ImageInfo
01791 *image_info;
01792
01793
01794
01795
01796 display=XOpenDisplay(draw_info->server_name);
01797 if (display == (Display *) NULL)
01798 {
01799 ThrowXWindowException(XServerError,"UnableToOpenXServer",
01800 draw_info->server_name);
01801 return(MagickFalse);
01802 }
01803
01804
01805
01806 (void) XSetErrorHandler(XError);
01807 image_info=AcquireImageInfo();
01808 client_name=GetClientName();
01809 resource_database=XGetResourceDatabase(display,client_name);
01810 XGetResourceInfo(image_info,resource_database,client_name,&resource_info);
01811 resource_info.close_server=MagickFalse;
01812 resource_info.colormap=PrivateColormap;
01813 resource_info.font=AcquireString(draw_info->font);
01814 resource_info.background_color=AcquireString("#ffffffffffff");
01815 resource_info.foreground_color=AcquireString("#000000000000");
01816 map_info=XAllocStandardColormap();
01817 if (map_info == (XStandardColormap *) NULL)
01818 {
01819 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
01820 image->filename);
01821 return(MagickFalse);
01822 }
01823
01824
01825
01826 visual_info=XBestVisualInfo(display,map_info,&resource_info);
01827 if (visual_info == (XVisualInfo *) NULL)
01828 {
01829 ThrowXWindowException(XServerError,"UnableToGetVisual",
01830 image->filename);
01831 return(MagickFalse);
01832 }
01833 map_info->colormap=(Colormap) NULL;
01834 pixel.pixels=(unsigned long *) NULL;
01835
01836
01837
01838 XGetMapInfo(visual_info,XDefaultColormap(display,visual_info->screen),
01839 map_info);
01840 XGetPixelPacket(display,visual_info,map_info,&resource_info,
01841 (Image *) NULL,&pixel);
01842 pixel.annotate_context=XDefaultGC(display,visual_info->screen);
01843
01844
01845
01846 font_info=XBestFont(display,&resource_info,MagickFalse);
01847 if (font_info == (XFontStruct *) NULL)
01848 {
01849 ThrowXWindowException(XServerError,"UnableToLoadFont",
01850 draw_info->font);
01851 return(MagickFalse);
01852 }
01853 if ((map_info == (XStandardColormap *) NULL) ||
01854 (visual_info == (XVisualInfo *) NULL) ||
01855 (font_info == (XFontStruct *) NULL))
01856 {
01857 XFreeResources(display,visual_info,map_info,&pixel,font_info,
01858 &resource_info,(XWindowInfo *) NULL);
01859 ThrowXWindowException(XServerError,"UnableToLoadFont",
01860 image->filename);
01861 return(MagickFalse);
01862 }
01863 cache_info=(*draw_info);
01864 }
01865
01866
01867
01868 XGetAnnotateInfo(&annotate_info);
01869 annotate_info.stencil=ForegroundStencil;
01870 if (cache_info.font != draw_info->font)
01871 {
01872
01873
01874
01875 (void) XFreeFont(display,font_info);
01876 (void) CloneString(&resource_info.font,draw_info->font);
01877 font_info=XBestFont(display,&resource_info,MagickFalse);
01878 if (font_info == (XFontStruct *) NULL)
01879 {
01880 ThrowXWindowException(XServerError,"UnableToLoadFont",
01881 draw_info->font);
01882 return(MagickFalse);
01883 }
01884 }
01885 if (image->debug != MagickFalse)
01886 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
01887 "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
01888 draw_info->font : "none",draw_info->pointsize);
01889 cache_info=(*draw_info);
01890 annotate_info.font_info=font_info;
01891 annotate_info.text=(char *) draw_info->text;
01892 annotate_info.width=(unsigned int) XTextWidth(font_info,draw_info->text,
01893 (int) strlen(draw_info->text));
01894 annotate_info.height=(unsigned int) font_info->ascent+font_info->descent;
01895 metrics->pixels_per_em.x=(double) font_info->max_bounds.width;
01896 metrics->pixels_per_em.y=(double) font_info->ascent+font_info->descent;
01897 metrics->ascent=(double) font_info->ascent+4;
01898 metrics->descent=(double) (-font_info->descent);
01899 metrics->width=annotate_info.width/ExpandAffine(&draw_info->affine);
01900 metrics->height=font_info->ascent+font_info->descent;
01901 metrics->max_advance=(double) font_info->max_bounds.width;
01902 metrics->bounds.x1=0.0;
01903 metrics->bounds.y1=metrics->descent;
01904 metrics->bounds.x2=metrics->ascent+metrics->descent;
01905 metrics->bounds.y2=metrics->ascent+metrics->descent;
01906 metrics->underline_position=(-2.0);
01907 metrics->underline_thickness=1.0;
01908 if (draw_info->render == MagickFalse)
01909 return(MagickTrue);
01910 if (draw_info->fill.opacity == TransparentOpacity)
01911 return(MagickTrue);
01912
01913
01914
01915 width=annotate_info.width;
01916 height=annotate_info.height;
01917 if ((draw_info->affine.rx != 0.0) || (draw_info->affine.ry != 0.0))
01918 {
01919 if (((draw_info->affine.sx-draw_info->affine.sy) == 0.0) &&
01920 ((draw_info->affine.rx+draw_info->affine.ry) == 0.0))
01921 annotate_info.degrees=(180.0/MagickPI)*
01922 atan2(draw_info->affine.rx,draw_info->affine.sx);
01923 }
01924 (void) FormatMagickString(annotate_info.geometry,MaxTextExtent,
01925 "%lux%lu+%ld+%ld",width,height,(long) (offset->x+0.5),
01926 (long) (offset->y-metrics->ascent-metrics->descent+
01927 draw_info->interline_spacing+0.5));
01928 pixel.pen_color.red=ScaleQuantumToShort(draw_info->fill.red);
01929 pixel.pen_color.green=ScaleQuantumToShort(draw_info->fill.green);
01930 pixel.pen_color.blue=ScaleQuantumToShort(draw_info->fill.blue);
01931 status=XAnnotateImage(display,&pixel,&annotate_info,image);
01932 if (status == 0)
01933 {
01934 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
01935 image->filename);
01936 return(MagickFalse);
01937 }
01938 return(MagickTrue);
01939 }
01940 #else
01941 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
01942 const PointInfo *offset,TypeMetric *metrics)
01943 {
01944 (void) draw_info;
01945 (void) offset;
01946 (void) metrics;
01947 (void) ThrowMagickException(&image->exception,GetMagickModule(),
01948 MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
01949 image->filename);
01950 return(MagickFalse);
01951 }
01952 #endif