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