Powered by Pair ImageMagick logo
Image Magick
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | File List | Namespace Members | Data Fields | Globals | Related Pages

ImageMagick-6.1.1/magick/annotate.c

Go to the documentation of this file.
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % AAA N N N N OOO TTTTT AAA TTTTT EEEEE % 00007 % A A NN N NN N O O T A A T E % 00008 % AAAAA N N N N N N O O T AAAAA T EEE % 00009 % A A N NN N NN O O T A A T E % 00010 % A A N N N N OOO T A A T EEEEE % 00011 % % 00012 % % 00013 % ImageMagick Image Annotation Methods % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1992 % 00018 % % 00019 % % 00020 % Copyright 1999-2004 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/www/Copyright.html % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % Digital Applications (www.digapp.com) contributed the stroked text algorithm. 00037 % It was written by Leonard Rosenthol. 00038 % 00039 % 00040 */ 00041 00042 /* 00043 Include declarations. 00044 */ 00045 #include "magick/studio.h" 00046 #include "magick/annotate.h" 00047 #include "magick/client.h" 00048 #include "magick/color.h" 00049 #include "magick/composite.h" 00050 #include "magick/composite_private.h" 00051 #include "magick/constitute.h" 00052 #include "magick/draw.h" 00053 #include "magick/draw_private.h" 00054 #include "magick/error.h" 00055 #include "magick/error_private.h" 00056 #include "magick/gem.h" 00057 #include "magick/geometry.h" 00058 #include "magick/log.h" 00059 #include "magick/resource_.h" 00060 #include "magick/string_.h" 00061 #include "magick/transform.h" 00062 #include "magick/type.h" 00063 #include "magick/utility.h" 00064 #include "magick/xwindow.h" 00065 #if defined(HasTTF) 00066 #if defined(__MINGW32__) 00067 # undef interface 00068 #endif 00069 #if defined(HAVE_FT2BUILD_H) 00070 # include <ft2build.h> 00071 #endif 00072 #if defined(FT_FREETYPE_H) 00073 # include FT_FREETYPE_H 00074 #else 00075 # include <freetype/freetype.h> 00076 #endif 00077 #if defined(FT_GLYPH_H) 00078 # include FT_GLYPH_H 00079 #else 00080 # include <freetype/ftglyph.h> 00081 #endif 00082 #if defined(FT_OUTLINE_H) 00083 # include FT_OUTLINE_H 00084 #else 00085 # include <freetype/ftoutln.h> 00086 #endif 00087 #if defined(FT_BBOX_H) 00088 # include FT_BBOX_H 00089 #else 00090 # include <freetype/ftbbox.h> 00091 #endif /* defined(FT_BBOX_H) */ 00092 #endif 00093 00094 /* 00095 Forward declarations. 00096 */ 00097 static MagickBooleanType 00098 RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *), 00099 RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *), 00100 RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *, 00101 TypeMetric *), 00102 RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *); 00103 00104 /* 00105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00106 % % 00107 % % 00108 % % 00109 % A n n o t a t e I m a g e % 00110 % % 00111 % % 00112 % % 00113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00114 % 00115 % AnnotateImage() annotates an image with text. Optionally you can include 00116 % any of the following bits of information about the image by embedding 00117 % the appropriate special characters: 00118 % 00119 % %b file size in bytes. 00120 % %c comment. 00121 % %d directory in which the image resides. 00122 % %e extension of the image file. 00123 % %f original filename of the image. 00124 % %h height of image. 00125 % %i filename of the image. 00126 % %k number of unique colors. 00127 % %l image label. 00128 % %m image file format. 00129 % %n number of images in a image sequence. 00130 % %o output image filename. 00131 % %p page number of the image. 00132 % %q image depth (8 or 16). 00133 % %p page number of the image. 00134 % %q image depth (8 or 16). 00135 % %s image scene number. 00136 % %t image filename without any extension. 00137 % %u a unique temporary filename. 00138 % %w image width. 00139 % %x x resolution of the image. 00140 % %y y resolution of the image. 00141 % 00142 % The format of the AnnotateImage method is: 00143 % 00144 % MagickBooleanType AnnotateImage(Image *image,DrawInfo *draw_info) 00145 % 00146 % A description of each parameter follows: 00147 % 00148 % o image: The image. 00149 % 00150 % o draw_info: The draw info. 00151 % 00152 % 00153 */ 00154 MagickExport MagickBooleanType AnnotateImage(Image *image, 00155 const DrawInfo *draw_info) 00156 { 00157 char 00158 primitive[MaxTextExtent], 00159 *text, 00160 **textlist; 00161 00162 DrawInfo 00163 *annotate, 00164 *annotate_info; 00165 00166 GeometryInfo 00167 geometry_info; 00168 00169 MagickBooleanType 00170 status; 00171 00172 PointInfo 00173 offset; 00174 00175 RectangleInfo 00176 geometry; 00177 00178 register long 00179 i; 00180 00181 size_t 00182 length; 00183 00184 TypeMetric 00185 metrics; 00186 00187 unsigned long 00188 height, 00189 number_lines; 00190 00191 /* 00192 Translate any embedded format characters (e.g. %f). 00193 */ 00194 assert(image != (Image *) NULL); 00195 assert(image->signature == MagickSignature); 00196 if (image->debug != MagickFalse) 00197 (void) LogMagickEvent(TraceEvent,GetMagickModule(),image->filename); 00198 assert(draw_info != (DrawInfo *) NULL); 00199 assert(draw_info->signature == MagickSignature); 00200 if (draw_info->text == (char *) NULL) 00201 return(MagickFalse); 00202 if (*draw_info->text == '\0') 00203 return(MagickFalse); 00204 text=TranslateText((ImageInfo *) NULL,image,draw_info->text); 00205 if (text == (char *) NULL) 00206 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00207 image->filename); 00208 textlist=StringToList(text); 00209 text=(char *) RelinquishMagickMemory(text); 00210 if (textlist == (char **) NULL) 00211 return(MagickFalse); 00212 length=strlen(textlist[0]); 00213 for (i=1; textlist[i] != (char *) NULL; i++) 00214 if (strlen(textlist[i]) > length) 00215 length=strlen(textlist[i]); 00216 number_lines=(unsigned long) i; 00217 text=(char *) AcquireMagickMemory(length+MaxTextExtent); 00218 if (text == (char *) NULL) 00219 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 00220 image->filename); 00221 annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info); 00222 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 00223 SetGeometry(image,&geometry); 00224 SetGeometryInfo(&geometry_info); 00225 if (annotate_info->geometry != (char *) NULL) 00226 { 00227 (void) ParsePageGeometry(image,annotate_info->geometry,&geometry); 00228 (void) ParseGeometry(annotate_info->geometry,&geometry_info); 00229 } 00230 image->storage_class=DirectClass; 00231 status=MagickTrue; 00232 for (i=0; textlist[i] != (char *) NULL; i++) 00233 { 00234 /* 00235 Position text relative to image. 00236 */ 00237 annotate_info->affine.tx=geometry_info.xi; 00238 annotate_info->affine.ty=geometry_info.psi; 00239 (void) CloneString(&annotate->text,textlist[i]); 00240 (void) GetTypeMetrics(image,annotate,&metrics); 00241 height=(unsigned long) (metrics.ascent-metrics.descent+0.5); 00242 switch (annotate->gravity) 00243 { 00244 case UndefinedGravity: 00245 default: 00246 { 00247 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height; 00248 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height; 00249 break; 00250 } 00251 case NorthWestGravity: 00252 { 00253 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i* 00254 annotate_info->affine.ry*height+annotate_info->affine.ry* 00255 (metrics.ascent+metrics.descent); 00256 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i* 00257 annotate_info->affine.sy*height+annotate_info->affine.sy* 00258 metrics.ascent; 00259 break; 00260 } 00261 case NorthGravity: 00262 { 00263 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+ 00264 geometry.width/2.0+i*annotate_info->affine.ry*height- 00265 annotate_info->affine.sx*metrics.width/2+annotate_info->affine.ry* 00266 (metrics.ascent+metrics.descent); 00267 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i* 00268 annotate_info->affine.sy*height+annotate_info->affine.sy* 00269 metrics.ascent-annotate_info->affine.rx*metrics.width/2.0; 00270 break; 00271 } 00272 case NorthEastGravity: 00273 { 00274 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+ 00275 geometry.width+i*annotate_info->affine.ry*height- 00276 annotate_info->affine.sx*metrics.width+annotate_info->affine.ry* 00277 (metrics.ascent+metrics.descent); 00278 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i* 00279 annotate_info->affine.sy*height+annotate_info->affine.sy* 00280 metrics.ascent-annotate_info->affine.rx*metrics.width; 00281 break; 00282 } 00283 case WestGravity: 00284 { 00285 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i* 00286 annotate_info->affine.ry*height+annotate_info->affine.ry* 00287 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0; 00288 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+ 00289 geometry.height/2.0+i*annotate_info->affine.sy*height+ 00290 annotate_info->affine.sy*(metrics.ascent+metrics.descent- 00291 (number_lines-1.0)*height)/2.0; 00292 break; 00293 } 00294 case StaticGravity: 00295 case CenterGravity: 00296 { 00297 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+ 00298 geometry.width/2.0+i*annotate_info->affine.ry*height- 00299 annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry* 00300 (metrics.ascent+metrics.descent-(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/2.0+annotate_info->affine.sy* 00304 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0; 00305 break; 00306 } 00307 case EastGravity: 00308 { 00309 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+ 00310 geometry.width+i*annotate_info->affine.ry*height- 00311 annotate_info->affine.sx*metrics.width+annotate_info->affine.ry* 00312 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.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+annotate_info->affine.sy* 00316 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0; 00317 break; 00318 } 00319 case SouthWestGravity: 00320 { 00321 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i* 00322 annotate_info->affine.ry*height-annotate_info->affine.ry* 00323 (number_lines-1.0)*height; 00324 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+ 00325 geometry.height+i*annotate_info->affine.sy*height- 00326 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent; 00327 break; 00328 } 00329 case SouthGravity: 00330 { 00331 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+ 00332 geometry.width/2.0+i*annotate_info->affine.ry*height- 00333 annotate_info->affine.sx*metrics.width/2.0-annotate_info->affine.ry* 00334 (number_lines-1.0)*height/2.0; 00335 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+ 00336 geometry.height+i*annotate_info->affine.sy*height- 00337 annotate_info->affine.rx*metrics.width/2.0-annotate_info->affine.sy* 00338 (number_lines-1.0)*height+metrics.descent; 00339 break; 00340 } 00341 case SouthEastGravity: 00342 { 00343 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+ 00344 geometry.width+i*annotate_info->affine.ry*height- 00345 annotate_info->affine.sx*metrics.width-annotate_info->affine.ry* 00346 (number_lines-1.0)*height; 00347 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+ 00348 geometry.height+i*annotate_info->affine.sy*height- 00349 annotate_info->affine.rx*metrics.width-annotate_info->affine.sy* 00350 (number_lines-1.0)*height+metrics.descent; 00351 break; 00352 } 00353 } 00354 switch (annotate->align) 00355 { 00356 case LeftAlign: 00357 { 00358 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height; 00359 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height; 00360 break; 00361 } 00362 case CenterAlign: 00363 { 00364 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height- 00365 annotate_info->affine.sx*metrics.width/2.0; 00366 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height- 00367 annotate_info->affine.rx*metrics.width/2.0; 00368 break; 00369 } 00370 case RightAlign: 00371 { 00372 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height- 00373 annotate_info->affine.sx*metrics.width; 00374 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height- 00375 annotate_info->affine.rx*metrics.width; 00376 break; 00377 } 00378 default: 00379 break; 00380 } 00381 if (draw_info->undercolor.opacity != TransparentOpacity) 00382 { 00383 /* 00384 Text box. 00385 */ 00386 annotate_info->fill=draw_info->undercolor; 00387 annotate_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent; 00388 annotate_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent; 00389 (void) FormatMagickString(primitive,MaxTextExtent, 00390 "rectangle 0,0 %g,%ld",metrics.width,height); 00391 (void) CloneString(&annotate_info->primitive,primitive); 00392 (void) DrawImage(image,annotate_info); 00393 } 00394 annotate_info->affine.tx=offset.x; 00395 annotate_info->affine.ty=offset.y; 00396 (void) FormatMagickString(primitive,MaxTextExtent, 00397 "stroke-width %g line 0,0 %g,0",metrics.underline_thickness, 00398 metrics.width); 00399 if (annotate->decorate == OverlineDecoration) 00400 { 00401 annotate_info->affine.ty-=(draw_info->affine.sy* 00402 (metrics.ascent+metrics.descent-metrics.underline_position+2)); 00403 (void) CloneString(&annotate_info->primitive,primitive); 00404 (void) DrawImage(image,annotate_info); 00405 } 00406 else 00407 if (annotate->decorate == UnderlineDecoration) 00408 { 00409 annotate_info->affine.ty-= 00410 (draw_info->affine.sy*metrics.underline_position); 00411 (void) CloneString(&annotate_info->primitive,primitive); 00412 (void) DrawImage(image,annotate_info); 00413 } 00414 /* 00415 Annotate image with text. 00416 */ 00417 status=RenderType(image,annotate,&offset,&metrics); 00418 if (status == MagickFalse) 00419 break; 00420 if (annotate->decorate == LineThroughDecoration) 00421 { 00422 annotate_info->affine.ty-=(draw_info->affine.sy*(height+ 00423 metrics.underline_position+metrics.descent)/2.0); 00424 (void) CloneString(&annotate_info->primitive,primitive); 00425 (void) DrawImage(image,annotate_info); 00426 } 00427 } 00428 /* 00429 Free resources. 00430 */ 00431 annotate_info=DestroyDrawInfo(annotate_info); 00432 annotate=DestroyDrawInfo(annotate); 00433 text=(char *) RelinquishMagickMemory(text); 00434 for (i=0; textlist[i] != (char *) NULL; i++) 00435 textlist[i]=(char *) RelinquishMagickMemory(textlist[i]); 00436 textlist=(char **) RelinquishMagickMemory(textlist); 00437 return(status); 00438 } 00439 00440 #if defined(HasTTF) 00441 /* 00442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00443 % % 00444 % % 00445 % % 00446 + E n c o d e S J I S % 00447 % % 00448 % % 00449 % % 00450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00451 % 00452 % EncodeSJIS() converts an ASCII text string to 2-bytes per character code 00453 % (like UCS-2). Returns the translated codes and the character count. 00454 % Characters under 0x7f are just copied, characters over 0x80 are tied with 00455 % the next character. 00456 % 00457 % Katsutoshi Shibuya contributed this method. 00458 % 00459 % The format of the EncodeSJIS function is: 00460 % 00461 % encoding=EncodeSJIS(const Image *image,const char *text,size_t count) 00462 % 00463 % A description of each parameter follows: 00464 % 00465 % o image: The image. 00466 % 00467 % o text: The text. 00468 % 00469 % o count: return the number of characters generated by the encoding. 00470 % 00471 % 00472 */ 00473 00474 static int GetOneCharacter(const unsigned char *text,size_t *length) 00475 { 00476 int 00477 c; 00478 00479 if (*length < 1) 00480 return(-1); 00481 c=text[0]; 00482 if ((c & 0x80) == 0) 00483 { 00484 *length=1; 00485 return((int) c); 00486 } 00487 if (*length < 2) 00488 { 00489 *length=0; 00490 return(-1); 00491 } 00492 *length=2; 00493 c=((int) (text[0]) << 8); 00494 c|=text[1]; 00495 return((int) c); 00496 } 00497 00498 static unsigned short *EncodeSJIS(const Image *image,const char *text, 00499 size_t *count) 00500 { 00501 int 00502 c; 00503 00504 register const char 00505 *p; 00506 00507 register unsigned short 00508 *q; 00509 00510 size_t 00511 length; 00512 00513 unsigned short 00514 *encoding; 00515 00516 *count=0; 00517 if ((text == (char *) NULL) || (*text == '\0')) 00518 return((unsigned short *) NULL); 00519 length=strlen(text)+MaxTextExtent; 00520 encoding=(unsigned short *) AcquireMagickMemory(length*sizeof(*encoding)); 00521 if (encoding == (unsigned short *) NULL) 00522 ThrowMagickFatalException(ResourceLimitFatalError,"MemoryAllocationFailed", 00523 image->filename); 00524 q=encoding; 00525 for (p=text; *p != '\0'; p+=length) 00526 { 00527 length=strlen(p); 00528 c=GetOneCharacter((const unsigned char *) p,&length); 00529 if (c < 0) 00530 { 00531 q=encoding; 00532 for (p=text; *p != '\0'; p++) 00533 *q++=(unsigned char) *p; 00534 break; 00535 } 00536 *q=(unsigned short) c; 00537 q++; 00538 } 00539 *count=q-encoding; 00540 return(encoding); 00541 } 00542 #endif 00543 00544 #if defined(HasTTF) 00545 /* 00546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00547 % % 00548 % % 00549 % % 00550 + E n c o d e T e x t % 00551 % % 00552 % % 00553 % % 00554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00555 % 00556 % EncodeText() converts an ASCII text string to wide text and returns the 00557 % translation and the character count. 00558 % 00559 % The format of the EncodeText function is: 00560 % 00561 % encoding=EncodeText(const Image *image,const char *text,size_t count) 00562 % 00563 % A description of each parameter follows: 00564 % 00565 % o image: The image. 00566 % 00567 % o text: The text. 00568 % 00569 % o count: return the number of characters generated by the encoding. 00570 % 00571 % 00572 */ 00573 static unsigned short *EncodeText(const Image *image,const char *text, 00574 size_t *count) 00575 { 00576 register const char 00577 *p; 00578 00579 register unsigned short 00580 *q; 00581 00582 unsigned short 00583 *encoding; 00584 00585 *count=0; 00586 if ((text == (char *) NULL) || (*text == '\0')) 00587 return((unsigned short *) NULL); 00588 encoding=(unsigned short *) 00589 AcquireMagickMemory((strlen(text)+MaxTextExtent)*sizeof(*encoding)); 00590 if (encoding == (unsigned short *) NULL) 00591 ThrowMagickFatalException(ResourceLimitFatalError,"MemoryAllocationFailed", 00592 image->filename); 00593 q=encoding; 00594 for (p=text; *p != '\0'; p++) 00595 *q++=(unsigned char) *p; 00596 *count=q-encoding; 00597 return(encoding); 00598 } 00599 #endif 00600 00601 #if defined(HasTTF) 00602 /* 00603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00604 % % 00605 % % 00606 % % 00607 + E n c o d e U n i c o d e % 00608 % % 00609 % % 00610 % % 00611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00612 % 00613 % EncodeUnicode() converts an ASCII text string to Unicode and returns the 00614 % Unicode translation and the character count. Characters under 0x7f are 00615 % just copied, characters over 0x80 are tied with the next character. 00616 % 00617 % The format of the EncodeUnicode function is: 00618 % 00619 % unicode=EncodeUnicode(const Image *image,const unsigned char *text, 00620 % size_t count) 00621 % 00622 % A description of each parameter follows: 00623 % 00624 % o unicode: EncodeUnicode() returns a pointer to an unsigned short array 00625 % array representing the encoded version of the ASCII string. 00626 % 00627 % o text: The text. 00628 % 00629 % o count: return the number of characters generated by the encoding. 00630 % 00631 % 00632 */ 00633 00634 static long GetUnicodeCharacter(const unsigned char *text,size_t *length) 00635 { 00636 unsigned long 00637 c; 00638 00639 if (*length < 1) 00640 return(-1); 00641 c=text[0]; 00642 if ((c & 0x80) == 0) 00643 { 00644 *length=1; 00645 return((long) c); 00646 } 00647 if ((*length < 2) || ((text[1] & 0xc0) != 0x80)) 00648 { 00649 *length=0; 00650 return(-1); 00651 } 00652 if ((c & 0xe0) != 0xe0) 00653 { 00654 *length=2; 00655 c=(text[0] & 0x1f) << 6; 00656 c|=text[1] & 0x3f; 00657 return((long) c); 00658 } 00659 if ((*length < 3) || ((text[2] & 0xc0) != 0x80)) 00660 { 00661 *length=0; 00662 return(-1); 00663 } 00664 if ((c & 0xf0) != 0xf0) 00665 { 00666 *length=3; 00667 c=(text[0] & 0xf) << 12; 00668 c|=(text[1] & 0x3f) << 6; 00669 c|=text[2] & 0x3f; 00670 return((long) c); 00671 } 00672 if ((*length < 4) || ((c & 0xf8) != 0xf0) || ((text[3] & 0xc0) != 0x80)) 00673 { 00674 *length=0; 00675 return(-1); 00676 } 00677 *length=4; 00678 c=(text[0] & 0x7) << 18; 00679 c|=(text[1] & 0x3f) << 12; 00680 c|=(text[2] & 0x3f) << 6; 00681 c|=text[3] & 0x3f; 00682 return((long) c); 00683 } 00684 00685 static unsigned short *EncodeUnicode(const Image *image,const char *text, 00686 size_t *count) 00687 { 00688 long 00689 c; 00690 00691 register const char 00692 *p; 00693 00694 register unsigned short 00695 *q; 00696 00697 size_t 00698 length; 00699 00700 unsigned short 00701 *unicode; 00702 00703 *count=0; 00704 if ((text == (char *) NULL) || (*text == '\0')) 00705 return((unsigned short *) NULL); 00706 length=strlen(text)+MaxTextExtent; 00707 unicode=(unsigned short *) AcquireMagickMemory(length*sizeof(*unicode)); 00708 if (unicode == (unsigned short *) NULL) 00709 ThrowMagickFatalException(ResourceLimitFatalError,"MemoryAllocationFailed", 00710 image->filename); 00711 q=unicode; 00712 for (p=text; *p != '\0'; p+=length) 00713 { 00714 length=strlen(p); 00715 c=GetUnicodeCharacter((const unsigned char *) p,&length); 00716 if (c < 0) 00717 { 00718 q=unicode; 00719 for (p=text; *p != '\0'; p++) 00720 *q++=(unsigned char) *p; 00721 break; 00722 } 00723 *q=(unsigned short) c; 00724 q++; 00725 } 00726 *count=q-unicode; 00727 return(unicode); 00728 } 00729 #endif 00730 00731 /* 00732 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00733 % % 00734 % % 00735 % % 00736 % G e t T y p e M e t r i c s % 00737 % % 00738 % % 00739 % % 00740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00741 % 00742 % GetTypeMetrics() returns the following information for the specified font 00743 % and text: 00744 % 00745 % o character width 00746 % o character height 00747 % o ascent 00748 % o descent 00749 % o text width 00750 % o text height 00751 % o maximum horizontal advance 00752 % o underline position 00753 % o underline thickness 00754 % 00755 % The format of the GetTypeMetrics method is: 00756 % 00757 % MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info, 00758 % TypeMetric *metrics) 00759 % 00760 % A description of each parameter follows: 00761 % 00762 % o image: The image. 00763 % 00764 % o draw_info: The draw info. 00765 % 00766 % o metrics: Return the font metrics in this structure. 00767 % 00768 % 00769 */ 00770 MagickExport MagickBooleanType GetTypeMetrics(Image *image, 00771 const DrawInfo *draw_info,TypeMetric *metrics) 00772 { 00773 DrawInfo 00774 *annotate_info; 00775 00776 MagickBooleanType 00777 status; 00778 00779 PointInfo 00780 offset; 00781 00782 assert(image != (Image *) NULL); 00783 assert(image->signature == MagickSignature); 00784 if (image->debug != MagickFalse) 00785 (void) LogMagickEvent(TraceEvent,GetMagickModule(),image->filename); 00786 assert(draw_info != (DrawInfo *) NULL); 00787 assert(draw_info->text != (char *) NULL); 00788 assert(draw_info->signature == MagickSignature); 00789 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 00790 annotate_info->render=MagickFalse; 00791 (void) ResetMagickMemory(metrics,0,sizeof(*metrics)); 00792 offset.x=0.0; 00793 offset.y=0.0; 00794 status=RenderType(image,annotate_info,&offset,metrics); 00795 annotate_info=DestroyDrawInfo(annotate_info); 00796 return(status); 00797 } 00798 00799 /* 00800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00801 % % 00802 % % 00803 % % 00804 + R e n d e r T y p e % 00805 % % 00806 % % 00807 % % 00808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00809 % 00810 % RenderType() renders text on the image. It also returns the bounding box of 00811 % the text relative to the image. 00812 % 00813 % The format of the RenderType method is: 00814 % 00815 % MagickBooleanType RenderType(Image *image,DrawInfo *draw_info, 00816 % const PointInfo *offset,TypeMetric *metrics) 00817 % 00818 % A description of each parameter follows: 00819 % 00820 % o image: The image. 00821 % 00822 % o draw_info: The draw info. 00823 % 00824 % o offset: (x,y) location of text relative to image. 00825 % 00826 % o metrics: bounding box of text. 00827 % 00828 % 00829 */ 00830 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info, 00831 const PointInfo *offset,TypeMetric *metrics) 00832 { 00833 const TypeInfo 00834 *type_info; 00835 00836 DrawInfo 00837 *annotate_info; 00838 00839 MagickBooleanType 00840 status; 00841 00842 type_info=(const TypeInfo *) NULL; 00843 if (draw_info->font != (char *) NULL) 00844 { 00845 if (*draw_info->font == '@') 00846 return(RenderFreetype(image,draw_info,(char *) NULL,offset,metrics)); 00847 if (*draw_info->font == '-') 00848 return(RenderX11(image,draw_info,offset,metrics)); 00849 type_info=GetTypeInfo(draw_info->font,&image->exception); 00850 if (type_info == (const TypeInfo *) NULL) 00851 if (IsAccessible(draw_info->font) != MagickFalse) 00852 return(RenderFreetype(image,draw_info,(char *) NULL,offset,metrics)); 00853 } 00854 if (type_info == (const TypeInfo *) NULL) 00855 type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style, 00856 draw_info->stretch,draw_info->weight,&image->exception); 00857 if (type_info == (const TypeInfo *) NULL) 00858 (void) ThrowMagickException(&image->exception,GetMagickModule(),TypeWarning, 00859 "UnableToReadFont",draw_info->font); 00860 if (type_info == (const TypeInfo *) NULL) 00861 type_info=GetTypeInfoByFamily("Arial",draw_info->style,draw_info->stretch, 00862 draw_info->weight,&image->exception); 00863 if (type_info == (const TypeInfo *) NULL) 00864 return(RenderPostscript(image,draw_info,offset,metrics)); 00865 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 00866 annotate_info->face=type_info->face; 00867 if (type_info->metrics != (char *) NULL) 00868 (void) CloneString(&annotate_info->metrics,type_info->metrics); 00869 if (type_info->glyphs != (char *) NULL) 00870 (void) CloneString(&annotate_info->font,type_info->glyphs); 00871 status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics); 00872 annotate_info=DestroyDrawInfo(annotate_info); 00873 return(status); 00874 } 00875 00876 /* 00877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00878 % % 00879 % % 00880 % % 00881 + R e n d e r F r e e t y p e % 00882 % % 00883 % % 00884 % % 00885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00886 % 00887 % RenderFreetype() renders text on the image with a Truetype font. It also 00888 % returns the bounding box of the text relative to the image. 00889 % 00890 % The format of the RenderFreetype method is: 00891 % 00892 % MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info, 00893 % const char *encoding,const PointInfo *offset,TypeMetric *metrics) 00894 % 00895 % A description of each parameter follows: 00896 % 00897 % o image: The image. 00898 % 00899 % o draw_info: The draw info. 00900 % 00901 % o encoding: The font encoding. 00902 % 00903 % o offset: (x,y) location of text relative to image. 00904 % 00905 % o metrics: bounding box of text. 00906 % 00907 % 00908 */ 00909 00910 #if defined(HasTTF) 00911 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to, 00912 DrawInfo *draw_info) 00913 { 00914 AffineMatrix 00915 affine; 00916 00917 char 00918 path[MaxTextExtent]; 00919 00920 affine=draw_info->affine; 00921 (void) FormatMagickString(path,MaxTextExtent,"C%g,%g %g,%g %g,%g", 00922 affine.tx+p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0, 00923 affine.ty-q->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0); 00924 (void) ConcatenateString(&draw_info->primitive,path); 00925 return(0); 00926 } 00927 00928 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info) 00929 { 00930 AffineMatrix 00931 affine; 00932 00933 char 00934 path[MaxTextExtent]; 00935 00936 affine=draw_info->affine; 00937 (void) FormatMagickString(path,MaxTextExtent,"L%g,%g",affine.tx+to->x/64.0, 00938 affine.ty-to->y/64.0); 00939 (void) ConcatenateString(&draw_info->primitive,path); 00940 return(0); 00941 } 00942 00943 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info) 00944 { 00945 AffineMatrix 00946 affine; 00947 00948 char 00949 path[MaxTextExtent]; 00950 00951 affine=draw_info->affine; 00952 (void) FormatMagickString(path,MaxTextExtent,"M%g,%g",affine.tx+to->x/64.0, 00953 affine.ty-to->y/64.0); 00954 (void) ConcatenateString(&draw_info->primitive,path); 00955 return(0); 00956 } 00957 00958 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to, 00959 DrawInfo *draw_info) 00960 { 00961 AffineMatrix 00962 affine; 00963 00964 char 00965 path[MaxTextExtent]; 00966 00967 affine=draw_info->affine; 00968 (void) FormatMagickString(path,MaxTextExtent,"Q%g,%g %g,%g", 00969 affine.tx+control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0, 00970 affine.ty-to->y/64.0); 00971 (void) ConcatenateString(&draw_info->primitive,path); 00972 return(0); 00973 } 00974 00975 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info, 00976 const char *encoding,const PointInfo *offset,TypeMetric *metrics) 00977 { 00978 #if !defined(FT_OPEN_PATHNAME) 00979 #define FT_OPEN_PATHNAME ft_open_pathname 00980 #endif 00981 00982 typedef struct _GlyphInfo 00983 { 00984 FT_UInt 00985 id; 00986 00987 FT_Vector 00988 origin; 00989 00990 FT_Glyph 00991 image; 00992 } GlyphInfo; 00993 00994 DrawInfo 00995 *annotate_info; 00996 00997 FT_BBox 00998 bounds; 00999 01000 FT_BitmapGlyph 01001 bitmap; 01002 01003 FT_Encoding 01004 encoding_type; 01005 01006 FT_Error 01007 status; 01008 01009 FT_Face 01010 face; 01011 01012 FT_Library 01013 library; 01014 01015 FT_Matrix 01016 affine; 01017 01018 FT_Open_Args 01019 args; 01020 01021 FT_Vector 01022 origin; 01023 01024 GlyphInfo 01025 glyph, 01026 last_glyph; 01027 01028 long 01029 y; 01030 01031 MagickBooleanType 01032 active; 01033 01034 MagickRealType 01035 fill_opacity; 01036 01037 PixelPacket 01038 fill_color; 01039 01040 PointInfo 01041 point, 01042 resolution; 01043 01044 register long 01045 i, 01046 x; 01047 01048 register PixelPacket 01049 *q; 01050 01051 register unsigned char 01052 *p; 01053 01054 size_t 01055 length; 01056 01057 static FT_Outline_Funcs 01058 OutlineMethods = 01059 { 01060 (FT_Outline_MoveTo_Func) TraceMoveTo, 01061 (FT_Outline_LineTo_Func) TraceLineTo, 01062 (FT_Outline_ConicTo_Func) TraceQuadraticBezier, 01063 (FT_Outline_CubicTo_Func) TraceCubicBezier, 01064 0, 0 01065 }; 01066 01067 unsigned short 01068 *text; 01069 01070 /* 01071 Initialize Truetype library. 01072 */ 01073 status=FT_Init_FreeType(&library); 01074 if (status) 01075 ThrowBinaryException(TypeError,"UnableToInitializeFreetypeLibrary", 01076 image->filename); 01077 args.flags=FT_OPEN_PATHNAME; 01078 if (*draw_info->font != '@') 01079 args.pathname=draw_info->font; 01080 else 01081 args.pathname=draw_info->font+1; 01082 status=FT_Open_Face(library,&args,draw_info->face,&face); 01083 if (status != 0) 01084 { 01085 (void) FT_Done_FreeType(library); 01086 ThrowBinaryException(TypeError,"UnableToReadFont",draw_info->font); 01087 } 01088 if ((draw_info->metrics != (char *) NULL) && 01089 (IsAccessible(draw_info->metrics) != MagickFalse)) 01090 (void) FT_Attach_File(face,draw_info->metrics); 01091 if (face->num_charmaps != 0) 01092 status=FT_Set_Charmap(face,face->charmaps[0]); 01093 encoding_type=ft_encoding_unicode; 01094 status=FT_Select_Charmap(face,encoding_type); 01095 if (status != 0) 01096 { 01097 encoding_type=ft_encoding_none; 01098 status=FT_Select_Charmap(face,encoding_type); 01099 } 01100 if (encoding != (char *) NULL) 01101 { 01102 if (LocaleCompare(encoding,"AdobeCustom") == 0) 01103 encoding_type=ft_encoding_adobe_custom; 01104 if (LocaleCompare(encoding,"AdobeExpert") == 0) 01105 encoding_type=ft_encoding_adobe_expert; 01106 if (LocaleCompare(encoding,"AdobeStandard") == 0) 01107 encoding_type=ft_encoding_adobe_standard; 01108 if (LocaleCompare(encoding,"AppleRoman") == 0) 01109 encoding_type=ft_encoding_apple_roman; 01110 if (LocaleCompare(encoding,"BIG5") == 0) 01111 encoding_type=ft_encoding_big5; 01112 if (LocaleCompare(encoding,"GB2312") == 0) 01113 encoding_type=ft_encoding_gb2312; 01114 if (LocaleCompare(encoding,"None") == 0) 01115 encoding_type=ft_encoding_none; 01116 if (LocaleCompare(encoding,"SJIScode") == 0) 01117 encoding_type=ft_encoding_sjis; 01118 if (LocaleCompare(encoding,"Symbol") == 0) 01119 encoding_type=ft_encoding_symbol; 01120 if (LocaleCompare(encoding,"Unicode") == 0) 01121 encoding_type=ft_encoding_unicode; 01122 if (LocaleCompare(encoding,"Wansung") == 0) 01123 encoding_type=ft_encoding_wansung; 01124 status=FT_Select_Charmap(face,encoding_type); 01125 if (status != 0) 01126 ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding); 01127 } 01128 /* 01129 Set text size. 01130 */ 01131 resolution.x=72.0; 01132 resolution.y=72.0; 01133 if (draw_info->density != (char *) NULL) 01134 { 01135 GeometryInfo 01136 geometry_info; 01137 01138 MagickStatusType 01139 flags; 01140 01141 flags=ParseGeometry(draw_info->density,&geometry_info); 01142 resolution.x=geometry_info.rho; 01143 resolution.y=geometry_info.sigma; 01144 if ((flags & SigmaValue) == 0) 01145 resolution.y=resolution.x; 01146 } 01147 (void) FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize), 01148 (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x, 01149 (FT_UInt) resolution.y); 01150 metrics->pixels_per_em.x=face->size->metrics.x_ppem; 01151 metrics->pixels_per_em.y=face->size->metrics.y_ppem; 01152 metrics->ascent=(double) face->size->metrics.ascender/64.0; 01153 metrics->descent=(double) face->size->metrics.descender/64.0; 01154 metrics->width=0; 01155 metrics->height=(double) face->size->metrics.height/64.0; 01156 metrics->max_advance=(double) face->size->metrics.max_advance/64.0; 01157 metrics->bounds.x1=0.0; 01158 metrics->bounds.y1=metrics->descent; 01159 metrics->bounds.x2=metrics->ascent+metrics->descent; 01160 metrics->bounds.y2=metrics->ascent+metrics->descent; 01161 metrics->underline_position=face->underline_position/64.0; 01162 metrics->underline_thickness=face->underline_thickness/64.0; 01163 if (*draw_info->text == '\0') 01164 { 01165 (void) FT_Done_Face(face); 01166 (void) FT_Done_FreeType(library); 01167 return(MagickTrue); 01168 } 01169 /* 01170 Convert text to 2-byte format as prescribed by the encoding. 01171 */ 01172 switch (encoding_type) 01173 { 01174 case ft_encoding_sjis: 01175 { 01176 text=EncodeSJIS(image,draw_info->text,&length); 01177 break; 01178 } 01179 case ft_encoding_unicode: 01180 { 01181 text=EncodeUnicode(image,draw_info->text,&length); 01182 break; 01183 } 01184 default: 01185 { 01186 if (draw_info->encoding != (char *) NULL) 01187 { 01188 if (LocaleCompare(draw_info->encoding,"SJIS") == 0) 01189 { 01190 text=EncodeSJIS(image,draw_info->text,&length); 01191 break; 01192 } 01193 if ((LocaleCompare(draw_info->encoding,"UTF-8") == 0) || 01194 (encoding_type != ft_encoding_none)) 01195 { 01196 text=EncodeUnicode(image,draw_info->text,&length); 01197 break; 01198 } 01199 } 01200 text=EncodeText(image,draw_info->text,&length); 01201 break; 01202 } 01203 } 01204 if (text == (unsigned short *) NULL) 01205 { 01206 (void) FT_Done_Face(face); 01207 (void) FT_Done_FreeType(library); 01208 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 01209 image->filename); 01210 } 01211 /* 01212 Compute bounding box. 01213 */ 01214 if (image->debug != MagickFalse) 01215 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; " 01216 "font-encoding %s; text-encoding %s; pointsize %g", 01217 draw_info->font != (char *) NULL ? draw_info->font : "none", 01218 encoding != (char *) NULL ? encoding : "none", 01219 draw_info->encoding != (char *) NULL ? draw_info->encoding : "none", 01220 draw_info->pointsize); 01221 glyph.id=0; 01222 glyph.image=NULL; 01223 last_glyph.id=0; 01224 last_glyph.image=NULL; 01225 origin.x=0; 01226 origin.y=0; 01227 affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5); 01228 affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5); 01229 affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5); 01230 affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5); 01231 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info); 01232 (void) QueryColorDatabase("#000000ff",&annotate_info->fill,&image->exception); 01233 (void) CloneString(&annotate_info->primitive,"path '"); 01234 if (image->matte == MagickFalse) 01235 SetImageOpacity(image,OpaqueOpacity); 01236 for (i=0; i < (long) length; i++) 01237 { 01238 glyph.id=FT_Get_Char_Index(face,text[i]); 01239 if ((glyph.id != 0) && (last_glyph.id != 0) && FT_HAS_KERNING(face)) 01240 { 01241 FT_Vector 01242 kerning; 01243 01244 (void) FT_Get_Kerning(face,last_glyph.id,glyph.id,ft_kerning_default, 01245 &kerning); 01246 origin.x+=kerning.x; 01247 } 01248 glyph.origin=origin; 01249 if (draw_info->render != MagickFalse) 01250 status=FT_Load_Glyph(face,glyph.id,FT_LOAD_DEFAULT); 01251 else 01252 status=FT_Load_Glyph(face,glyph.id,FT_LOAD_NO_BITMAP); 01253 if (status != MagickFalse) 01254 continue; 01255 status=FT_Get_Glyph(face->glyph,&glyph.image); 01256 if (status != MagickFalse) 01257 continue; 01258 (void) FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->outline, 01259 &bounds); 01260 if ((i == 0) || (bounds.xMin < metrics->bounds.x1)) 01261 metrics->bounds.x1=bounds.xMin; 01262 if ((i == 0) || (bounds.yMin < metrics->bounds.y1)) 01263 metrics->bounds.y1=bounds.yMin; 01264 if ((i == 0) || (bounds.xMax > metrics->bounds.x2)) 01265 metrics->bounds.x2=bounds.xMax; 01266 if ((i == 0) || (bounds.yMax > metrics->bounds.y2)) 01267 metrics->bounds.y2=bounds.yMax; 01268 if (draw_info->render != MagickFalse) 01269 if ((draw_info->stroke.opacity != TransparentOpacity) || 01270 (draw_info->stroke_pattern != (Image *) NULL)) 01271 { 01272 /* 01273 Trace the glyph. 01274 */ 01275 annotate_info->affine.tx=glyph.origin.x/64.0; 01276 annotate_info->affine.ty=glyph.origin.y/64.0; 01277 (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->outline, 01278 &OutlineMethods,annotate_info); 01279 } 01280 FT_Vector_Transform(&glyph.origin,&affine); 01281 (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin); 01282 if (draw_info->render != MagickFalse) 01283 { 01284 /* 01285 Rasterize the glyph. 01286 */ 01287 status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal, 01288 (FT_Vector *) NULL,MagickTrue); 01289 if (status != MagickFalse) 01290 continue; 01291 bitmap=(FT_BitmapGlyph) glyph.image; 01292 image->storage_class=DirectClass; 01293 point.x=offset->x+bitmap->left; 01294 point.y=offset->y-bitmap->top; 01295 p=bitmap->bitmap.buffer; 01296 for (y=0; y < (long) bitmap->bitmap.rows; y++) 01297 { 01298 if (((long) (point.y+y+0.5) < 0) || 01299 ((long) (point.y+y+0.5) >= (long) image->rows)) 01300 { 01301 p+=bitmap->bitmap.width; 01302 continue; 01303 } 01304 q=GetImagePixels(image,(long) (point.x+0.5),(long) (point.y+y+0.5), 01305 bitmap->bitmap.width,1); 01306 active=q != (PixelPacket *) NULL ? MagickTrue : MagickFalse; 01307 for (x=0; x < (long) bitmap->bitmap.width; x++) 01308 { 01309 if (((long) (point.x+x+0.5) < 0) || (*p == 0) || 01310 ((unsigned long) (point.x+x+0.5) >= image->columns)) 01311 { 01312 p++; 01313 q++; 01314 continue; 01315 } 01316 if (draw_info->text_antialias) 01317 fill_opacity=(MagickRealType) (*p)/255.0; 01318 else 01319 fill_opacity=(*p) > 127 ? 1.0 : 0.0; 01320 if (active ==