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