1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % AAA N N N N OOO TTTTT AAA TTTTT EEEEE %
7 % A A NN N NN N O O T A A T E %
8 % AAAAA N N N N N N O O T AAAAA T EEE %
9 % A A N NN N NN O O T A A T E %
10 % A A N N N N OOO T A A T EEEEE %
11 % %
12 % %
13 % MagickCore Image Annotation Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % Digital Applications (www.digapp.com) contributed the stroked text algorithm.
37 % It was written by Leonard Rosenthol.
38 %
39 %
40 */
41
42 /*
43 Include declarations.
44 */
45 #include "MagickCore/studio.h"
46 #include "MagickCore/annotate.h"
47 #include "MagickCore/annotate-private.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/cache-private.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/composite.h"
57 #include "MagickCore/composite-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/draw-private.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/gem.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/log.h"
68 #include "MagickCore/quantum.h"
69 #include "MagickCore/quantum-private.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/property.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/semaphore.h"
74 #include "MagickCore/statistic.h"
75 #include "MagickCore/string_.h"
76 #include "MagickCore/token.h"
77 #include "MagickCore/token-private.h"
78 #include "MagickCore/transform.h"
79 #include "MagickCore/transform-private.h"
80 #include "MagickCore/type.h"
81 #include "MagickCore/utility.h"
82 #include "MagickCore/utility-private.h"
83 #include "MagickCore/xwindow.h"
84 #include "MagickCore/xwindow-private.h"
85 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
86 #if defined(__MINGW32__)
87 # undef interface
88 #endif
89 #include <ft2build.h>
90 #if defined(FT_FREETYPE_H)
91 # include FT_FREETYPE_H
92 #else
93 # include <freetype/freetype.h>
94 #endif
95 #if defined(FT_GLYPH_H)
96 # include FT_GLYPH_H
97 #else
98 # include <freetype/ftglyph.h>
99 #endif
100 #if defined(FT_OUTLINE_H)
101 # include FT_OUTLINE_H
102 #else
103 # include <freetype/ftoutln.h>
104 #endif
105 #if defined(FT_BBOX_H)
106 # include FT_BBOX_H
107 #else
108 # include <freetype/ftbbox.h>
109 #endif /* defined(FT_BBOX_H) */
110 #endif
111 #if defined(MAGICKCORE_RAQM_DELEGATE)
112 #include <raqm.h>
113 #endif
114 typedef struct _GraphemeInfo
115 {
116 size_t
117 index,
118 x_offset,
119 x_advance,
120 y_offset;
121
122 size_t
123 cluster;
124 } GraphemeInfo;
125
126 /*
127 Annotate semaphores.
128 */
129 static SemaphoreInfo
130 *annotate_semaphore = (SemaphoreInfo *) NULL;
131
132 /*
133 Forward declarations.
134 */
135 static MagickBooleanType
136 RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
137 ExceptionInfo *),
138 RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
139 ExceptionInfo *),
140 RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
141 TypeMetric *,ExceptionInfo *),
142 RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
143 ExceptionInfo *);
144
145 /*
146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147 % %
148 % %
149 % %
150 + A n n o t a t e C o m p o n e n t G e n e s i s %
151 % %
152 % %
153 % %
154 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155 %
156 % AnnotateComponentGenesis() instantiates the annotate component.
157 %
158 % The format of the AnnotateComponentGenesis method is:
159 %
160 % MagickBooleanType AnnotateComponentGenesis(void)
161 %
162 */
AnnotateComponentGenesis(void)163 MagickPrivate MagickBooleanType AnnotateComponentGenesis(void)
164 {
165 if (annotate_semaphore == (SemaphoreInfo *) NULL)
166 annotate_semaphore=AcquireSemaphoreInfo();
167 return(MagickTrue);
168 }
169
170 /*
171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172 % %
173 % %
174 % %
175 + 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 %
176 % %
177 % %
178 % %
179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180 %
181 % AnnotateComponentTerminus() destroys the annotate component.
182 %
183 % The format of the AnnotateComponentTerminus method is:
184 %
185 % AnnotateComponentTerminus(void)
186 %
187 */
AnnotateComponentTerminus(void)188 MagickPrivate void AnnotateComponentTerminus(void)
189 {
190 if (annotate_semaphore == (SemaphoreInfo *) NULL)
191 ActivateSemaphoreInfo(&annotate_semaphore);
192 RelinquishSemaphoreInfo(&annotate_semaphore);
193 }
194
195 /*
196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197 % %
198 % %
199 % %
200 % A n n o t a t e I m a g e %
201 % %
202 % %
203 % %
204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205 %
206 % AnnotateImage() annotates an image with text. Optionally you can include
207 % any of the following bits of information about the image by embedding
208 % the appropriate special characters:
209 %
210 % \n newline
211 % \r carriage return
212 % < less-than character.
213 % > greater-than character.
214 % & ampersand character.
215 % %% a percent sign
216 % %b file size of image read in
217 % %c comment meta-data property
218 % %d directory component of path
219 % %e filename extension or suffix
220 % %f filename (including suffix)
221 % %g layer canvas page geometry (equivalent to "%Wx%H%X%Y")
222 % %h current image height in pixels
223 % %i image filename (note: becomes output filename for "info:")
224 % %k CALCULATED: number of unique colors
225 % %l label meta-data property
226 % %m image file format (file magic)
227 % %n number of images in current image sequence
228 % %o output filename (used for delegates)
229 % %p index of image in current image list
230 % %q quantum depth (compile-time constant)
231 % %r image class and colorspace
232 % %s scene number (from input unless re-assigned)
233 % %t filename without directory or extension (suffix)
234 % %u unique temporary filename (used for delegates)
235 % %w current width in pixels
236 % %x x resolution (density)
237 % %y y resolution (density)
238 % %z image depth (as read in unless modified, image save depth)
239 % %A image transparency channel enabled (true/false)
240 % %C image compression type
241 % %D image GIF dispose method
242 % %G original image size (%wx%h; before any resizes)
243 % %H page (canvas) height
244 % %M Magick filename (original file exactly as given, including read mods)
245 % %O page (canvas) offset ( = %X%Y )
246 % %P page (canvas) size ( = %Wx%H )
247 % %Q image compression quality ( 0 = default )
248 % %S ?? scenes ??
249 % %T image time delay (in centi-seconds)
250 % %U image resolution units
251 % %W page (canvas) width
252 % %X page (canvas) x offset (including sign)
253 % %Y page (canvas) y offset (including sign)
254 % %Z unique filename (used for delegates)
255 % %@ CALCULATED: trim bounding box (without actually trimming)
256 % %# CALCULATED: 'signature' hash of image values
257 %
258 % The format of the AnnotateImage method is:
259 %
260 % MagickBooleanType AnnotateImage(Image *image,DrawInfo *draw_info,
261 % ExceptionInfo *exception)
262 %
263 % A description of each parameter follows:
264 %
265 % o image: the image.
266 %
267 % o draw_info: the draw info.
268 %
269 % o exception: return any errors or warnings in this structure.
270 %
271 */
AnnotateImage(Image * image,const DrawInfo * draw_info,ExceptionInfo * exception)272 MagickExport MagickBooleanType AnnotateImage(Image *image,
273 const DrawInfo *draw_info,ExceptionInfo *exception)
274 {
275 char
276 primitive[MagickPathExtent],
277 **textlist;
278
279 DrawInfo
280 *annotate,
281 *annotate_info;
282
283 GeometryInfo
284 geometry_info;
285
286 MagickBooleanType
287 status;
288
289 PointInfo
290 offset;
291
292 RectangleInfo
293 geometry;
294
295 register ssize_t
296 i;
297
298 size_t
299 length;
300
301 TypeMetric
302 metrics;
303
304 size_t
305 height,
306 number_lines;
307
308 assert(image != (Image *) NULL);
309 assert(image->signature == MagickCoreSignature);
310 if (image->debug != MagickFalse)
311 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
312 assert(draw_info != (DrawInfo *) NULL);
313 assert(draw_info->signature == MagickCoreSignature);
314 if (draw_info->text == (char *) NULL)
315 return(MagickFalse);
316 if (*draw_info->text == '\0')
317 return(MagickTrue);
318 textlist=StringToList(draw_info->text);
319 if (textlist == (char **) NULL)
320 return(MagickFalse);
321 length=strlen(textlist[0]);
322 for (i=1; textlist[i] != (char *) NULL; i++)
323 if (strlen(textlist[i]) > length)
324 length=strlen(textlist[i]);
325 number_lines=(size_t) i;
326 annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
327 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
328 SetGeometry(image,&geometry);
329 SetGeometryInfo(&geometry_info);
330 if (annotate_info->geometry != (char *) NULL)
331 {
332 (void) ParsePageGeometry(image,annotate_info->geometry,&geometry,
333 exception);
334 (void) ParseGeometry(annotate_info->geometry,&geometry_info);
335 }
336 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
337 return(MagickFalse);
338 if (IsGrayColorspace(image->colorspace) != MagickFalse)
339 (void) SetImageColorspace(image,sRGBColorspace,exception);
340 status=MagickTrue;
341 for (i=0; textlist[i] != (char *) NULL; i++)
342 {
343 /*
344 Position text relative to image.
345 */
346 annotate_info->affine.tx=geometry_info.xi-image->page.x;
347 annotate_info->affine.ty=geometry_info.psi-image->page.y;
348 (void) CloneString(&annotate->text,textlist[i]);
349 (void) GetTypeMetrics(image,annotate,&metrics,exception);
350 height=(ssize_t) (metrics.ascent-metrics.descent+
351 draw_info->interline_spacing+0.5);
352 switch (annotate->gravity)
353 {
354 case UndefinedGravity:
355 default:
356 {
357 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
358 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
359 break;
360 }
361 case NorthWestGravity:
362 {
363 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
364 annotate_info->affine.ry*height+annotate_info->affine.ry*
365 (metrics.ascent+metrics.descent);
366 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
367 annotate_info->affine.sy*height+annotate_info->affine.sy*
368 metrics.ascent;
369 break;
370 }
371 case NorthGravity:
372 {
373 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
374 geometry.width/2.0+i*annotate_info->affine.ry*height-
375 annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
376 (metrics.ascent+metrics.descent);
377 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
378 annotate_info->affine.sy*height+annotate_info->affine.sy*
379 metrics.ascent-annotate_info->affine.rx*metrics.width/2.0;
380 break;
381 }
382 case NorthEastGravity:
383 {
384 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
385 geometry.width+i*annotate_info->affine.ry*height-
386 annotate_info->affine.sx*metrics.width+annotate_info->affine.ry*
387 (metrics.ascent+metrics.descent)-1.0;
388 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
389 annotate_info->affine.sy*height+annotate_info->affine.sy*
390 metrics.ascent-annotate_info->affine.rx*metrics.width;
391 break;
392 }
393 case WestGravity:
394 {
395 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
396 annotate_info->affine.ry*height+annotate_info->affine.ry*
397 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
398 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
399 geometry.height/2.0+i*annotate_info->affine.sy*height+
400 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
401 (number_lines-1.0)*height)/2.0;
402 break;
403 }
404 case CenterGravity:
405 {
406 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
407 geometry.width/2.0+i*annotate_info->affine.ry*height-
408 annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
409 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
410 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
411 geometry.height/2.0+i*annotate_info->affine.sy*height-
412 annotate_info->affine.rx*metrics.width/2.0+annotate_info->affine.sy*
413 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
414 break;
415 }
416 case EastGravity:
417 {
418 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
419 geometry.width+i*annotate_info->affine.ry*height-
420 annotate_info->affine.sx*metrics.width+
421 annotate_info->affine.ry*(metrics.ascent+metrics.descent-
422 (number_lines-1.0)*height)/2.0-1.0;
423 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
424 geometry.height/2.0+i*annotate_info->affine.sy*height-
425 annotate_info->affine.rx*metrics.width+
426 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
427 (number_lines-1.0)*height)/2.0;
428 break;
429 }
430 case SouthWestGravity:
431 {
432 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
433 annotate_info->affine.ry*height-annotate_info->affine.ry*
434 (number_lines-1.0)*height;
435 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
436 geometry.height+i*annotate_info->affine.sy*height-
437 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
438 break;
439 }
440 case SouthGravity:
441 {
442 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
443 geometry.width/2.0+i*annotate_info->affine.ry*height-
444 annotate_info->affine.sx*metrics.width/2.0-
445 annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
446 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
447 geometry.height+i*annotate_info->affine.sy*height-
448 annotate_info->affine.rx*metrics.width/2.0-
449 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
450 break;
451 }
452 case SouthEastGravity:
453 {
454 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
455 geometry.width+i*annotate_info->affine.ry*height-
456 annotate_info->affine.sx*metrics.width-
457 annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
458 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
459 geometry.height+i*annotate_info->affine.sy*height-
460 annotate_info->affine.rx*metrics.width-
461 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
462 break;
463 }
464 }
465 switch (annotate->align)
466 {
467 case LeftAlign:
468 {
469 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
470 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
471 break;
472 }
473 case CenterAlign:
474 {
475 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
476 annotate_info->affine.sx*metrics.width/2.0;
477 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
478 annotate_info->affine.rx*metrics.width/2.0;
479 break;
480 }
481 case RightAlign:
482 {
483 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
484 annotate_info->affine.sx*metrics.width;
485 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
486 annotate_info->affine.rx*metrics.width;
487 break;
488 }
489 default:
490 break;
491 }
492 if (draw_info->undercolor.alpha != TransparentAlpha)
493 {
494 DrawInfo
495 *undercolor_info;
496
497 /*
498 Text box.
499 */
500 undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
501 undercolor_info->fill=draw_info->undercolor;
502 undercolor_info->affine=draw_info->affine;
503 undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
504 undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
505 (void) FormatLocaleString(primitive,MagickPathExtent,
506 "rectangle 0.0,0.0 %g,%g",metrics.origin.x,(double) height);
507 (void) CloneString(&undercolor_info->primitive,primitive);
508 (void) DrawImage(image,undercolor_info,exception);
509 (void) DestroyDrawInfo(undercolor_info);
510 }
511 annotate_info->affine.tx=offset.x;
512 annotate_info->affine.ty=offset.y;
513 (void) FormatLocaleString(primitive,MagickPathExtent,"stroke-width %g "
514 "line 0,0 %g,0",metrics.underline_thickness,metrics.width);
515 if (annotate->decorate == OverlineDecoration)
516 {
517 annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
518 metrics.descent-metrics.underline_position));
519 (void) CloneString(&annotate_info->primitive,primitive);
520 (void) DrawImage(image,annotate_info,exception);
521 }
522 else
523 if (annotate->decorate == UnderlineDecoration)
524 {
525 annotate_info->affine.ty-=(draw_info->affine.sy*
526 metrics.underline_position);
527 (void) CloneString(&annotate_info->primitive,primitive);
528 (void) DrawImage(image,annotate_info,exception);
529 }
530 /*
531 Annotate image with text.
532 */
533 status=RenderType(image,annotate,&offset,&metrics,exception);
534 if (status == MagickFalse)
535 break;
536 if (annotate->decorate == LineThroughDecoration)
537 {
538 annotate_info->affine.ty-=(draw_info->affine.sy*(height+
539 metrics.underline_position+metrics.descent)/2.0);
540 (void) CloneString(&annotate_info->primitive,primitive);
541 (void) DrawImage(image,annotate_info,exception);
542 }
543 }
544 /*
545 Relinquish resources.
546 */
547 annotate_info=DestroyDrawInfo(annotate_info);
548 annotate=DestroyDrawInfo(annotate);
549 for (i=0; textlist[i] != (char *) NULL; i++)
550 textlist[i]=DestroyString(textlist[i]);
551 textlist=(char **) RelinquishMagickMemory(textlist);
552 return(status);
553 }
554
555 /*
556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
557 % %
558 % %
559 % %
560 % F o r m a t M a g i c k C a p t i o n %
561 % %
562 % %
563 % %
564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
565 %
566 % FormatMagickCaption() formats a caption so that it fits within the image
567 % width. It returns the number of lines in the formatted caption.
568 %
569 % The format of the FormatMagickCaption method is:
570 %
571 % ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
572 % const MagickBooleanType split,TypeMetric *metrics,char **caption,
573 % ExceptionInfo *exception)
574 %
575 % A description of each parameter follows.
576 %
577 % o image: The image.
578 %
579 % o draw_info: the draw info.
580 %
581 % o split: when no convenient line breaks-- insert newline.
582 %
583 % o metrics: Return the font metrics in this structure.
584 %
585 % o caption: the caption.
586 %
587 % o exception: return any errors or warnings in this structure.
588 %
589 */
FormatMagickCaption(Image * image,DrawInfo * draw_info,const MagickBooleanType split,TypeMetric * metrics,char ** caption,ExceptionInfo * exception)590 MagickExport ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
591 const MagickBooleanType split,TypeMetric *metrics,char **caption,
592 ExceptionInfo *exception)
593 {
594 MagickBooleanType
595 status;
596
597 register char
598 *p,
599 *q,
600 *s;
601
602 register ssize_t
603 i;
604
605 size_t
606 width;
607
608 ssize_t
609 n;
610
611 q=draw_info->text;
612 s=(char *) NULL;
613 for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
614 {
615 if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
616 s=p;
617 if (GetUTFCode(p) == '\n')
618 {
619 q=draw_info->text;
620 continue;
621 }
622 for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
623 *q++=(*(p+i));
624 *q='\0';
625 status=GetTypeMetrics(image,draw_info,metrics,exception);
626 if (status == MagickFalse)
627 break;
628 width=(size_t) floor(metrics->width+draw_info->stroke_width+0.5);
629 if (width <= image->columns)
630 continue;
631 if (s != (char *) NULL)
632 {
633 *s='\n';
634 p=s;
635 }
636 else
637 if (split != MagickFalse)
638 {
639 /*
640 No convenient line breaks-- insert newline.
641 */
642 n=p-(*caption);
643 if ((n > 0) && ((*caption)[n-1] != '\n'))
644 {
645 char
646 *target;
647
648 target=AcquireString(*caption);
649 CopyMagickString(target,*caption,n+1);
650 ConcatenateMagickString(target,"\n",strlen(*caption)+1);
651 ConcatenateMagickString(target,p,strlen(*caption)+2);
652 (void) DestroyString(*caption);
653 *caption=target;
654 p=(*caption)+n;
655 }
656 }
657 q=draw_info->text;
658 s=(char *) NULL;
659 }
660 n=0;
661 for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
662 if (GetUTFCode(p) == '\n')
663 n++;
664 return(n);
665 }
666
667 /*
668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669 % %
670 % %
671 % %
672 % G e t M u l t i l i n e T y p e M e t r i c s %
673 % %
674 % %
675 % %
676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
677 %
678 % GetMultilineTypeMetrics() returns the following information for the
679 % specified font and text:
680 %
681 % character width
682 % character height
683 % ascender
684 % descender
685 % text width
686 % text height
687 % maximum horizontal advance
688 % bounds: x1
689 % bounds: y1
690 % bounds: x2
691 % bounds: y2
692 % origin: x
693 % origin: y
694 % underline position
695 % underline thickness
696 %
697 % This method is like GetTypeMetrics() but it returns the maximum text width
698 % and height for multiple lines of text.
699 %
700 % The format of the GetMultilineTypeMetrics method is:
701 %
702 % MagickBooleanType GetMultilineTypeMetrics(Image *image,
703 % const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
704 %
705 % A description of each parameter follows:
706 %
707 % o image: the image.
708 %
709 % o draw_info: the draw info.
710 %
711 % o metrics: Return the font metrics in this structure.
712 %
713 % o exception: return any errors or warnings in this structure.
714 %
715 */
GetMultilineTypeMetrics(Image * image,const DrawInfo * draw_info,TypeMetric * metrics,ExceptionInfo * exception)716 MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
717 const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
718 {
719 char
720 **textlist;
721
722 DrawInfo
723 *annotate_info;
724
725 MagickBooleanType
726 status;
727
728 register ssize_t
729 i;
730
731 size_t
732 height,
733 count;
734
735 TypeMetric
736 extent;
737
738 assert(image != (Image *) NULL);
739 assert(image->signature == MagickCoreSignature);
740 if (image->debug != MagickFalse)
741 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
742 assert(draw_info != (DrawInfo *) NULL);
743 assert(draw_info->text != (char *) NULL);
744 assert(draw_info->signature == MagickCoreSignature);
745 if (*draw_info->text == '\0')
746 return(MagickFalse);
747 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
748 annotate_info->text=DestroyString(annotate_info->text);
749 /*
750 Convert newlines to multiple lines of text.
751 */
752 textlist=StringToStrings(draw_info->text,&count);
753 if (textlist == (char **) NULL)
754 return(MagickFalse);
755 annotate_info->render=MagickFalse;
756 annotate_info->direction=UndefinedDirection;
757 (void) memset(metrics,0,sizeof(*metrics));
758 (void) memset(&extent,0,sizeof(extent));
759 /*
760 Find the widest of the text lines.
761 */
762 annotate_info->text=textlist[0];
763 status=GetTypeMetrics(image,annotate_info,&extent,exception);
764 *metrics=extent;
765 height=(count*(size_t) (metrics->ascent-metrics->descent+
766 0.5)+(count-1)*draw_info->interline_spacing);
767 if (AcquireMagickResource(HeightResource,height) == MagickFalse)
768 {
769 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
770 "WidthOrHeightExceedsLimit","`%s'",image->filename);
771 status=MagickFalse;
772 }
773 else
774 {
775 for (i=1; i < (ssize_t) count; i++)
776 {
777 annotate_info->text=textlist[i];
778 status=GetTypeMetrics(image,annotate_info,&extent,exception);
779 if (status == MagickFalse)
780 break;
781 if (extent.width > metrics->width)
782 *metrics=extent;
783 if (AcquireMagickResource(WidthResource,extent.width) == MagickFalse)
784 {
785 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
786 "WidthOrHeightExceedsLimit","`%s'",image->filename);
787 status=MagickFalse;
788 break;
789 }
790 }
791 metrics->height=(double) height;
792 }
793 /*
794 Relinquish resources.
795 */
796 annotate_info->text=(char *) NULL;
797 annotate_info=DestroyDrawInfo(annotate_info);
798 for (i=0; i < (ssize_t) count; i++)
799 textlist[i]=DestroyString(textlist[i]);
800 textlist=(char **) RelinquishMagickMemory(textlist);
801 return(status);
802 }
803
804 /*
805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806 % %
807 % %
808 % %
809 % G e t T y p e M e t r i c s %
810 % %
811 % %
812 % %
813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 %
815 % GetTypeMetrics() returns the following information for the specified font
816 % and text:
817 %
818 % character width
819 % character height
820 % ascender
821 % descender
822 % text width
823 % text height
824 % maximum horizontal advance
825 % bounds: x1
826 % bounds: y1
827 % bounds: x2
828 % bounds: y2
829 % origin: x
830 % origin: y
831 % underline position
832 % underline thickness
833 %
834 % The format of the GetTypeMetrics method is:
835 %
836 % MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
837 % TypeMetric *metrics,ExceptionInfo *exception)
838 %
839 % A description of each parameter follows:
840 %
841 % o image: the image.
842 %
843 % o draw_info: the draw info.
844 %
845 % o metrics: Return the font metrics in this structure.
846 %
847 % o exception: return any errors or warnings in this structure.
848 %
849 */
GetTypeMetrics(Image * image,const DrawInfo * draw_info,TypeMetric * metrics,ExceptionInfo * exception)850 MagickExport MagickBooleanType GetTypeMetrics(Image *image,
851 const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
852 {
853 DrawInfo
854 *annotate_info;
855
856 MagickBooleanType
857 status;
858
859 PointInfo
860 offset;
861
862 assert(image != (Image *) NULL);
863 assert(image->signature == MagickCoreSignature);
864 if (image->debug != MagickFalse)
865 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
866 assert(draw_info != (DrawInfo *) NULL);
867 assert(draw_info->text != (char *) NULL);
868 assert(draw_info->signature == MagickCoreSignature);
869 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
870 annotate_info->render=MagickFalse;
871 annotate_info->direction=UndefinedDirection;
872 (void) memset(metrics,0,sizeof(*metrics));
873 offset.x=0.0;
874 offset.y=0.0;
875 status=RenderType(image,annotate_info,&offset,metrics,exception);
876 if (image->debug != MagickFalse)
877 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
878 "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
879 "bounds: %g,%g %g,%g; origin: %g,%g; pixels per em: %g,%g; "
880 "underline position: %g; underline thickness: %g",annotate_info->text,
881 metrics->width,metrics->height,metrics->ascent,metrics->descent,
882 metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
883 metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
884 metrics->pixels_per_em.x,metrics->pixels_per_em.y,
885 metrics->underline_position,metrics->underline_thickness);
886 annotate_info=DestroyDrawInfo(annotate_info);
887 return(status);
888 }
889
890 /*
891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
892 % %
893 % %
894 % %
895 + R e n d e r T y p e %
896 % %
897 % %
898 % %
899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 %
901 % RenderType() renders text on the image. It also returns the bounding box of
902 % the text relative to the image.
903 %
904 % The format of the RenderType method is:
905 %
906 % MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
907 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
908 %
909 % A description of each parameter follows:
910 %
911 % o image: the image.
912 %
913 % o draw_info: the draw info.
914 %
915 % o offset: (x,y) location of text relative to image.
916 %
917 % o metrics: bounding box of text.
918 %
919 % o exception: return any errors or warnings in this structure.
920 %
921 */
RenderType(Image * image,const DrawInfo * draw_info,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)922 static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
923 const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
924 {
925 const TypeInfo
926 *type_info;
927
928 DrawInfo
929 *annotate_info;
930
931 MagickBooleanType
932 status;
933
934 type_info=(const TypeInfo *) NULL;
935 if (draw_info->font != (char *) NULL)
936 {
937 if (*draw_info->font == '@')
938 {
939 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
940 metrics,exception);
941 return(status);
942 }
943 if (*draw_info->font == '-')
944 return(RenderX11(image,draw_info,offset,metrics,exception));
945 if (*draw_info->font == '^')
946 return(RenderPostscript(image,draw_info,offset,metrics,exception));
947 if (IsPathAccessible(draw_info->font) != MagickFalse)
948 {
949 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
950 metrics,exception);
951 return(status);
952 }
953 type_info=GetTypeInfo(draw_info->font,exception);
954 if (type_info == (const TypeInfo *) NULL)
955 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
956 "UnableToReadFont","`%s'",draw_info->font);
957 }
958 if ((type_info == (const TypeInfo *) NULL) &&
959 (draw_info->family != (const char *) NULL))
960 {
961 type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
962 draw_info->stretch,draw_info->weight,exception);
963 if (type_info == (const TypeInfo *) NULL)
964 {
965 char
966 **family;
967
968 int
969 number_families;
970
971 register ssize_t
972 i;
973
974 /*
975 Parse font family list.
976 */
977 family=StringToArgv(draw_info->family,&number_families);
978 for (i=1; i < (ssize_t) number_families; i++)
979 {
980 type_info=GetTypeInfoByFamily(family[i],draw_info->style,
981 draw_info->stretch,draw_info->weight,exception);
982 if (type_info != (const TypeInfo *) NULL)
983 break;
984 }
985 for (i=0; i < (ssize_t) number_families; i++)
986 family[i]=DestroyString(family[i]);
987 family=(char **) RelinquishMagickMemory(family);
988 if (type_info == (const TypeInfo *) NULL)
989 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
990 "UnableToReadFont","`%s'",draw_info->family);
991 }
992 }
993 if (type_info == (const TypeInfo *) NULL)
994 type_info=GetTypeInfoByFamily("Arial",draw_info->style,
995 draw_info->stretch,draw_info->weight,exception);
996 if (type_info == (const TypeInfo *) NULL)
997 type_info=GetTypeInfoByFamily("Helvetica",draw_info->style,
998 draw_info->stretch,draw_info->weight,exception);
999 if (type_info == (const TypeInfo *) NULL)
1000 type_info=GetTypeInfoByFamily("Century Schoolbook",draw_info->style,
1001 draw_info->stretch,draw_info->weight,exception);
1002 if (type_info == (const TypeInfo *) NULL)
1003 type_info=GetTypeInfoByFamily("Sans",draw_info->style,
1004 draw_info->stretch,draw_info->weight,exception);
1005 if (type_info == (const TypeInfo *) NULL)
1006 type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
1007 draw_info->stretch,draw_info->weight,exception);
1008 if (type_info == (const TypeInfo *) NULL)
1009 type_info=GetTypeInfo("*",exception);
1010 if (type_info == (const TypeInfo *) NULL)
1011 {
1012 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics,
1013 exception);
1014 return(status);
1015 }
1016 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1017 annotate_info->face=type_info->face;
1018 if (type_info->metrics != (char *) NULL)
1019 (void) CloneString(&annotate_info->metrics,type_info->metrics);
1020 if (type_info->glyphs != (char *) NULL)
1021 (void) CloneString(&annotate_info->font,type_info->glyphs);
1022 status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics,
1023 exception);
1024 annotate_info=DestroyDrawInfo(annotate_info);
1025 return(status);
1026 }
1027
1028 /*
1029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1030 % %
1031 % %
1032 % %
1033 + R e n d e r F r e e t y p e %
1034 % %
1035 % %
1036 % %
1037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1038 %
1039 % RenderFreetype() renders text on the image with a Truetype font. It also
1040 % returns the bounding box of the text relative to the image.
1041 %
1042 % The format of the RenderFreetype method is:
1043 %
1044 % MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
1045 % const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1046 % ExceptionInfo *exception)
1047 %
1048 % A description of each parameter follows:
1049 %
1050 % o image: the image.
1051 %
1052 % o draw_info: the draw info.
1053 %
1054 % o encoding: the font encoding.
1055 %
1056 % o offset: (x,y) location of text relative to image.
1057 %
1058 % o metrics: bounding box of text.
1059 %
1060 % o exception: return any errors or warnings in this structure.
1061 %
1062 */
1063
1064 #if defined(MAGICKCORE_FREETYPE_DELEGATE)
1065
ComplexTextLayout(const Image * image,const DrawInfo * draw_info,const char * text,const size_t length,const FT_Face face,const FT_Int32 flags,GraphemeInfo ** grapheme,ExceptionInfo * exception)1066 static size_t ComplexTextLayout(const Image *image,const DrawInfo *draw_info,
1067 const char *text,const size_t length,const FT_Face face,const FT_Int32 flags,
1068 GraphemeInfo **grapheme,ExceptionInfo *exception)
1069 {
1070 #if defined(MAGICKCORE_RAQM_DELEGATE)
1071 const char
1072 *features;
1073
1074 raqm_t
1075 *rq;
1076
1077 raqm_glyph_t
1078 *glyphs;
1079
1080 register size_t
1081 i;
1082
1083 size_t
1084 extent;
1085
1086 extent=0;
1087 rq=raqm_create();
1088 if (rq == (raqm_t *) NULL)
1089 goto cleanup;
1090 if (raqm_set_text_utf8(rq,text,length) == 0)
1091 goto cleanup;
1092 if (raqm_set_par_direction(rq,(raqm_direction_t) draw_info->direction) == 0)
1093 goto cleanup;
1094 if (raqm_set_freetype_face(rq,face) == 0)
1095 goto cleanup;
1096 features=GetImageProperty(image,"type:features",exception);
1097 if (features != (const char *) NULL)
1098 {
1099 char
1100 breaker,
1101 quote,
1102 *token;
1103
1104 int
1105 next,
1106 status_token;
1107
1108 TokenInfo
1109 *token_info;
1110
1111 next=0;
1112 token_info=AcquireTokenInfo();
1113 token=AcquireString("");
1114 status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1115 &breaker,&next,"e);
1116 while (status_token == 0)
1117 {
1118 raqm_add_font_feature(rq,token,strlen(token));
1119 status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1120 &breaker,&next,"e);
1121 }
1122 token_info=DestroyTokenInfo(token_info);
1123 token=DestroyString(token);
1124 }
1125 if (raqm_layout(rq) == 0)
1126 goto cleanup;
1127 glyphs=raqm_get_glyphs(rq,&extent);
1128 if (glyphs == (raqm_glyph_t *) NULL)
1129 {
1130 extent=0;
1131 goto cleanup;
1132 }
1133 *grapheme=(GraphemeInfo *) AcquireQuantumMemory(extent,sizeof(**grapheme));
1134 if (*grapheme == (GraphemeInfo *) NULL)
1135 {
1136 extent=0;
1137 goto cleanup;
1138 }
1139 for (i=0; i < (ssize_t) extent; i++)
1140 {
1141 (*grapheme)[i].index=glyphs[i].index;
1142 (*grapheme)[i].x_offset=glyphs[i].x_offset;
1143 (*grapheme)[i].x_advance=glyphs[i].x_advance;
1144 (*grapheme)[i].y_offset=glyphs[i].y_offset;
1145 (*grapheme)[i].cluster=glyphs[i].cluster;
1146 }
1147
1148 cleanup:
1149 raqm_destroy(rq);
1150 return(extent);
1151 #else
1152 const char
1153 *p;
1154
1155 FT_Error
1156 ft_status;
1157
1158 register ssize_t
1159 i;
1160
1161 ssize_t
1162 last_glyph;
1163
1164 magick_unreferenced(image);
1165 magick_unreferenced(exception);
1166
1167 /*
1168 Simple layout for bi-directional text (right-to-left or left-to-right).
1169 */
1170 *grapheme=(GraphemeInfo *) AcquireQuantumMemory(length+1,sizeof(**grapheme));
1171 if (*grapheme == (GraphemeInfo *) NULL)
1172 return(0);
1173 last_glyph=0;
1174 p=text;
1175 for (i=0; GetUTFCode(p) != 0; p+=GetUTFOctets(p), i++)
1176 {
1177 (*grapheme)[i].index=(ssize_t) FT_Get_Char_Index(face,GetUTFCode(p));
1178 (*grapheme)[i].x_offset=0;
1179 (*grapheme)[i].y_offset=0;
1180 if (((*grapheme)[i].index != 0) && (last_glyph != 0))
1181 {
1182 if (FT_HAS_KERNING(face))
1183 {
1184 FT_Vector
1185 kerning;
1186
1187 ft_status=FT_Get_Kerning(face,(FT_UInt) last_glyph,(FT_UInt)
1188 (*grapheme)[i].index,ft_kerning_default,&kerning);
1189 if (ft_status == 0)
1190 (*grapheme)[i-1].x_advance+=(FT_Pos) ((draw_info->direction ==
1191 RightToLeftDirection ? -1.0 : 1.0)*kerning.x);
1192 }
1193 }
1194 ft_status=FT_Load_Glyph(face,(FT_UInt) (*grapheme)[i].index,flags);
1195 (*grapheme)[i].x_advance=face->glyph->advance.x;
1196 (*grapheme)[i].cluster=p-text;
1197 last_glyph=(*grapheme)[i].index;
1198 }
1199 return((size_t) i);
1200 #endif
1201 }
1202
TraceCubicBezier(FT_Vector * p,FT_Vector * q,FT_Vector * to,DrawInfo * draw_info)1203 static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
1204 DrawInfo *draw_info)
1205 {
1206 AffineMatrix
1207 affine;
1208
1209 char
1210 path[MagickPathExtent];
1211
1212 affine=draw_info->affine;
1213 (void) FormatLocaleString(path,MagickPathExtent,"C%g,%g %g,%g %g,%g",
1214 affine.tx+p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,affine.ty-
1215 q->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1216 (void) ConcatenateString(&draw_info->primitive,path);
1217 return(0);
1218 }
1219
TraceLineTo(FT_Vector * to,DrawInfo * draw_info)1220 static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
1221 {
1222 AffineMatrix
1223 affine;
1224
1225 char
1226 path[MagickPathExtent];
1227
1228 affine=draw_info->affine;
1229 (void) FormatLocaleString(path,MagickPathExtent,"L%g,%g",affine.tx+to->x/64.0,
1230 affine.ty-to->y/64.0);
1231 (void) ConcatenateString(&draw_info->primitive,path);
1232 return(0);
1233 }
1234
TraceMoveTo(FT_Vector * to,DrawInfo * draw_info)1235 static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
1236 {
1237 AffineMatrix
1238 affine;
1239
1240 char
1241 path[MagickPathExtent];
1242
1243 affine=draw_info->affine;
1244 (void) FormatLocaleString(path,MagickPathExtent,"M%g,%g",affine.tx+to->x/64.0,
1245 affine.ty-to->y/64.0);
1246 (void) ConcatenateString(&draw_info->primitive,path);
1247 return(0);
1248 }
1249
TraceQuadraticBezier(FT_Vector * control,FT_Vector * to,DrawInfo * draw_info)1250 static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
1251 DrawInfo *draw_info)
1252 {
1253 AffineMatrix
1254 affine;
1255
1256 char
1257 path[MagickPathExtent];
1258
1259 affine=draw_info->affine;
1260 (void) FormatLocaleString(path,MagickPathExtent,"Q%g,%g %g,%g",affine.tx+
1261 control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,affine.ty-
1262 to->y/64.0);
1263 (void) ConcatenateString(&draw_info->primitive,path);
1264 return(0);
1265 }
1266
RenderFreetype(Image * image,const DrawInfo * draw_info,const char * encoding,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)1267 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1268 const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1269 ExceptionInfo *exception)
1270 {
1271 #if !defined(FT_OPEN_PATHNAME)
1272 #define FT_OPEN_PATHNAME ft_open_pathname
1273 #endif
1274
1275 typedef struct _GlyphInfo
1276 {
1277 FT_UInt
1278 id;
1279
1280 FT_Vector
1281 origin;
1282
1283 FT_Glyph
1284 image;
1285 } GlyphInfo;
1286
1287 const char
1288 *value;
1289
1290 DrawInfo
1291 *annotate_info;
1292
1293 FT_BBox
1294 bounds;
1295
1296 FT_BitmapGlyph
1297 bitmap;
1298
1299 FT_Encoding
1300 encoding_type;
1301
1302 FT_Error
1303 ft_status;
1304
1305 FT_Face
1306 face;
1307
1308 FT_Int32
1309 flags;
1310
1311 FT_Library
1312 library;
1313
1314 FT_Matrix
1315 affine;
1316
1317 FT_Open_Args
1318 args;
1319
1320 FT_Vector
1321 origin;
1322
1323 GlyphInfo
1324 glyph,
1325 last_glyph;
1326
1327 GraphemeInfo
1328 *grapheme;
1329
1330 MagickBooleanType
1331 status;
1332
1333 PointInfo
1334 point,
1335 resolution;
1336
1337 register char
1338 *p;
1339
1340 register ssize_t
1341 i;
1342
1343 size_t
1344 length;
1345
1346 ssize_t
1347 code,
1348 y;
1349
1350 static FT_Outline_Funcs
1351 OutlineMethods =
1352 {
1353 (FT_Outline_MoveTo_Func) TraceMoveTo,
1354 (FT_Outline_LineTo_Func) TraceLineTo,
1355 (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1356 (FT_Outline_CubicTo_Func) TraceCubicBezier,
1357 0, 0
1358 };
1359
1360 unsigned char
1361 *utf8;
1362
1363 /*
1364 Initialize Truetype library.
1365 */
1366 ft_status=FT_Init_FreeType(&library);
1367 if (ft_status != 0)
1368 ThrowBinaryException(TypeError,"UnableToInitializeFreetypeLibrary",
1369 image->filename);
1370 args.flags=FT_OPEN_PATHNAME;
1371 if (draw_info->font == (char *) NULL)
1372 args.pathname=ConstantString("helvetica");
1373 else
1374 if (*draw_info->font != '@')
1375 args.pathname=ConstantString(draw_info->font);
1376 else
1377 args.pathname=ConstantString(draw_info->font+1);
1378 face=(FT_Face) NULL;
1379 ft_status=FT_Open_Face(library,&args,(long) draw_info->face,&face);
1380 if (ft_status != 0)
1381 {
1382 (void) FT_Done_FreeType(library);
1383 (void) ThrowMagickException(exception,GetMagickModule(),TypeError,
1384 "UnableToReadFont","`%s'",args.pathname);
1385 args.pathname=DestroyString(args.pathname);
1386 return(MagickFalse);
1387 }
1388 args.pathname=DestroyString(args.pathname);
1389 if ((draw_info->metrics != (char *) NULL) &&
1390 (IsPathAccessible(draw_info->metrics) != MagickFalse))
1391 (void) FT_Attach_File(face,draw_info->metrics);
1392 encoding_type=FT_ENCODING_UNICODE;
1393 ft_status=FT_Select_Charmap(face,encoding_type);
1394 if ((ft_status != 0) && (face->num_charmaps != 0))
1395 ft_status=FT_Set_Charmap(face,face->charmaps[0]);
1396 if (encoding != (const char *) NULL)
1397 {
1398 if (LocaleCompare(encoding,"AdobeCustom") == 0)
1399 encoding_type=FT_ENCODING_ADOBE_CUSTOM;
1400 if (LocaleCompare(encoding,"AdobeExpert") == 0)
1401 encoding_type=FT_ENCODING_ADOBE_EXPERT;
1402 if (LocaleCompare(encoding,"AdobeStandard") == 0)
1403 encoding_type=FT_ENCODING_ADOBE_STANDARD;
1404 if (LocaleCompare(encoding,"AppleRoman") == 0)
1405 encoding_type=FT_ENCODING_APPLE_ROMAN;
1406 if (LocaleCompare(encoding,"BIG5") == 0)
1407 encoding_type=FT_ENCODING_BIG5;
1408 #if defined(FT_ENCODING_PRC)
1409 if (LocaleCompare(encoding,"GB2312") == 0)
1410 encoding_type=FT_ENCODING_PRC;
1411 #endif
1412 #if defined(FT_ENCODING_JOHAB)
1413 if (LocaleCompare(encoding,"Johab") == 0)
1414 encoding_type=FT_ENCODING_JOHAB;
1415 #endif
1416 #if defined(FT_ENCODING_ADOBE_LATIN_1)
1417 if (LocaleCompare(encoding,"Latin-1") == 0)
1418 encoding_type=FT_ENCODING_ADOBE_LATIN_1;
1419 #endif
1420 #if defined(FT_ENCODING_ADOBE_LATIN_2)
1421 if (LocaleCompare(encoding,"Latin-2") == 0)
1422 encoding_type=FT_ENCODING_OLD_LATIN_2;
1423 #endif
1424 if (LocaleCompare(encoding,"None") == 0)
1425 encoding_type=FT_ENCODING_NONE;
1426 if (LocaleCompare(encoding,"SJIScode") == 0)
1427 encoding_type=FT_ENCODING_SJIS;
1428 if (LocaleCompare(encoding,"Symbol") == 0)
1429 encoding_type=FT_ENCODING_MS_SYMBOL;
1430 if (LocaleCompare(encoding,"Unicode") == 0)
1431 encoding_type=FT_ENCODING_UNICODE;
1432 if (LocaleCompare(encoding,"Wansung") == 0)
1433 encoding_type=FT_ENCODING_WANSUNG;
1434 ft_status=FT_Select_Charmap(face,encoding_type);
1435 if (ft_status != 0)
1436 {
1437 (void) FT_Done_Face(face);
1438 (void) FT_Done_FreeType(library);
1439 ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
1440 }
1441 }
1442 /*
1443 Set text size.
1444 */
1445 resolution.x=DefaultResolution;
1446 resolution.y=DefaultResolution;
1447 if (draw_info->density != (char *) NULL)
1448 {
1449 GeometryInfo
1450 geometry_info;
1451
1452 MagickStatusType
1453 geometry_flags;
1454
1455 geometry_flags=ParseGeometry(draw_info->density,&geometry_info);
1456 resolution.x=geometry_info.rho;
1457 resolution.y=geometry_info.sigma;
1458 if ((geometry_flags & SigmaValue) == 0)
1459 resolution.y=resolution.x;
1460 }
1461 ft_status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1462 (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1463 (FT_UInt) resolution.y);
1464 if (ft_status != 0)
1465 {
1466 (void) FT_Done_Face(face);
1467 (void) FT_Done_FreeType(library);
1468 ThrowBinaryException(TypeError,"UnableToReadFont",draw_info->font);
1469 }
1470 metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1471 metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1472 metrics->ascent=(double) face->size->metrics.ascender/64.0;
1473 metrics->descent=(double) face->size->metrics.descender/64.0;
1474 metrics->width=0;
1475 metrics->origin.x=0;
1476 metrics->origin.y=0;
1477 metrics->height=(double) face->size->metrics.height/64.0;
1478 metrics->max_advance=0.0;
1479 if (face->size->metrics.max_advance > MagickEpsilon)
1480 metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1481 metrics->bounds.x1=0.0;
1482 metrics->bounds.y1=metrics->descent;
1483 metrics->bounds.x2=metrics->ascent+metrics->descent;
1484 metrics->bounds.y2=metrics->ascent+metrics->descent;
1485 metrics->underline_position=face->underline_position/64.0;
1486 metrics->underline_thickness=face->underline_thickness/64.0;
1487 if ((draw_info->text == (char *) NULL) || (*draw_info->text == '\0'))
1488 {
1489 (void) FT_Done_Face(face);
1490 (void) FT_Done_FreeType(library);
1491 return(MagickTrue);
1492 }
1493 /*
1494 Compute bounding box.
1495 */
1496 if (image->debug != MagickFalse)
1497 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
1498 "font-encoding %s; text-encoding %s; pointsize %g",
1499 draw_info->font != (char *) NULL ? draw_info->font : "none",
1500 encoding != (char *) NULL ? encoding : "none",
1501 draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1502 draw_info->pointsize);
1503 flags=FT_LOAD_DEFAULT;
1504 if (draw_info->render == MagickFalse)
1505 flags=FT_LOAD_NO_BITMAP;
1506 if (draw_info->text_antialias == MagickFalse)
1507 flags|=FT_LOAD_TARGET_MONO;
1508 else
1509 {
1510 #if defined(FT_LOAD_TARGET_LIGHT)
1511 flags|=FT_LOAD_TARGET_LIGHT;
1512 #elif defined(FT_LOAD_TARGET_LCD)
1513 flags|=FT_LOAD_TARGET_LCD;
1514 #endif
1515 }
1516 value=GetImageProperty(image,"type:hinting",exception);
1517 if ((value != (const char *) NULL) && (LocaleCompare(value,"off") == 0))
1518 flags|=FT_LOAD_NO_HINTING;
1519 glyph.id=0;
1520 glyph.image=NULL;
1521 last_glyph.id=0;
1522 last_glyph.image=NULL;
1523 origin.x=0;
1524 origin.y=0;
1525 affine.xx=65536L;
1526 affine.yx=0L;
1527 affine.xy=0L;
1528 affine.yy=65536L;
1529 if (draw_info->render != MagickFalse)
1530 {
1531 affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1532 affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1533 affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1534 affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1535 }
1536 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1537 if (annotate_info->dash_pattern != (double *) NULL)
1538 annotate_info->dash_pattern[0]=0.0;
1539 (void) CloneString(&annotate_info->primitive,"path '");
1540 status=MagickTrue;
1541 if (draw_info->render != MagickFalse)
1542 {
1543 if (image->storage_class != DirectClass)
1544 (void) SetImageStorageClass(image,DirectClass,exception);
1545 if (image->alpha_trait == UndefinedPixelTrait)
1546 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1547 }
1548 point.x=0.0;
1549 point.y=0.0;
1550 for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
1551 if (GetUTFCode(p) < 0)
1552 break;
1553 utf8=(unsigned char *) NULL;
1554 if (GetUTFCode(p) == 0)
1555 p=draw_info->text;
1556 else
1557 {
1558 utf8=ConvertLatin1ToUTF8((unsigned char *) draw_info->text);
1559 if (utf8 != (unsigned char *) NULL)
1560 p=(char *) utf8;
1561 }
1562 grapheme=(GraphemeInfo *) NULL;
1563 length=ComplexTextLayout(image,draw_info,p,strlen(p),face,flags,&grapheme,
1564 exception);
1565 code=0;
1566 for (i=0; i < (ssize_t) length; i++)
1567 {
1568 FT_Outline
1569 outline;
1570
1571 /*
1572 Render UTF-8 sequence.
1573 */
1574 glyph.id=(FT_UInt) grapheme[i].index;
1575 if (glyph.id == 0)
1576 glyph.id=FT_Get_Char_Index(face,' ');
1577 if ((glyph.id != 0) && (last_glyph.id != 0))
1578 origin.x+=(FT_Pos) (64.0*draw_info->kerning);
1579 glyph.origin=origin;
1580 glyph.origin.x+=(FT_Pos) grapheme[i].x_offset;
1581 glyph.origin.y+=(FT_Pos) grapheme[i].y_offset;
1582 glyph.image=0;
1583 ft_status=FT_Load_Glyph(face,glyph.id,flags);
1584 if (ft_status != 0)
1585 continue;
1586 ft_status=FT_Get_Glyph(face->glyph,&glyph.image);
1587 if (ft_status != 0)
1588 continue;
1589 outline=((FT_OutlineGlyph) glyph.image)->outline;
1590 ft_status=FT_Outline_Get_BBox(&outline,&bounds);
1591 if (ft_status != 0)
1592 continue;
1593 if ((p == draw_info->text) || (bounds.xMin < metrics->bounds.x1))
1594 if (bounds.xMin != 0)
1595 metrics->bounds.x1=(double) bounds.xMin;
1596 if ((p == draw_info->text) || (bounds.yMin < metrics->bounds.y1))
1597 if (bounds.yMin != 0)
1598 metrics->bounds.y1=(double) bounds.yMin;
1599 if ((p == draw_info->text) || (bounds.xMax > metrics->bounds.x2))
1600 if (bounds.xMax != 0)
1601 metrics->bounds.x2=(double) bounds.xMax;
1602 if ((p == draw_info->text) || (bounds.yMax > metrics->bounds.y2))
1603 if (bounds.yMax != 0)
1604 metrics->bounds.y2=(double) bounds.yMax;
1605 if (((draw_info->stroke.alpha != TransparentAlpha) ||
1606 (draw_info->stroke_pattern != (Image *) NULL)) &&
1607 ((status != MagickFalse) && (draw_info->render != MagickFalse)))
1608 {
1609 /*
1610 Trace the glyph.
1611 */
1612 annotate_info->affine.tx=glyph.origin.x/64.0;
1613 annotate_info->affine.ty=(-glyph.origin.y/64.0);
1614 if ((outline.n_contours > 0) && (outline.n_points > 0))
1615 ft_status=FT_Outline_Decompose(&outline,&OutlineMethods,
1616 annotate_info);
1617 }
1618 FT_Vector_Transform(&glyph.origin,&affine);
1619 (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1620 ft_status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
1621 (FT_Vector *) NULL,MagickTrue);
1622 if (ft_status != 0)
1623 continue;
1624 bitmap=(FT_BitmapGlyph) glyph.image;
1625 point.x=offset->x+bitmap->left;
1626 if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1627 point.x=offset->x+(origin.x >> 6);
1628 point.y=offset->y-bitmap->top;
1629 if (draw_info->render != MagickFalse)
1630 {
1631 CacheView
1632 *image_view;
1633
1634 MagickBooleanType
1635 transparent_fill;
1636
1637 register unsigned char
1638 *r;
1639
1640 /*
1641 Rasterize the glyph.
1642 */
1643 transparent_fill=((draw_info->fill.alpha == TransparentAlpha) &&
1644 (draw_info->fill_pattern == (Image *) NULL) &&
1645 (draw_info->stroke.alpha == TransparentAlpha) &&
1646 (draw_info->stroke_pattern == (Image *) NULL)) ? MagickTrue :
1647 MagickFalse;
1648 image_view=AcquireAuthenticCacheView(image,exception);
1649 r=bitmap->bitmap.buffer;
1650 for (y=0; y < (ssize_t) bitmap->bitmap.rows; y++)
1651 {
1652 double
1653 fill_opacity;
1654
1655 MagickBooleanType
1656 active,
1657 sync;
1658
1659 PixelInfo
1660 fill_color;
1661
1662 register Quantum
1663 *magick_restrict q;
1664
1665 register ssize_t
1666 x;
1667
1668 ssize_t
1669 n,
1670 x_offset,
1671 y_offset;
1672
1673 if (status == MagickFalse)
1674 continue;
1675 x_offset=(ssize_t) ceil(point.x-0.5);
1676 y_offset=(ssize_t) ceil(point.y+y-0.5);
1677 if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
1678 continue;
1679 q=(Quantum *) NULL;
1680 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1681 active=MagickFalse;
1682 else
1683 {
1684 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
1685 bitmap->bitmap.width,1,exception);
1686 active=q != (Quantum *) NULL ? MagickTrue : MagickFalse;
1687 }
1688 n=y*bitmap->bitmap.pitch-1;
1689 for (x=0; x < (ssize_t) bitmap->bitmap.width; x++)
1690 {
1691 n++;
1692 x_offset++;
1693 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1694 {
1695 if (q != (Quantum *) NULL)
1696 q+=GetPixelChannels(image);
1697 continue;
1698 }
1699 if (bitmap->bitmap.pixel_mode != ft_pixel_mode_mono)
1700 fill_opacity=(double) (r[n])/(bitmap->bitmap.num_grays-1);
1701 else
1702 fill_opacity=((r[(x >> 3)+y*bitmap->bitmap.pitch] &
1703 (1 << (~x & 0x07)))) == 0 ? 0.0 : 1.0;
1704 if (draw_info->text_antialias == MagickFalse)
1705 fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
1706 if (active == MagickFalse)
1707 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
1708 exception);
1709 if (q == (Quantum *) NULL)
1710 continue;
1711 if (transparent_fill == MagickFalse)
1712 {
1713 GetPixelInfo(image,&fill_color);
1714 GetFillColor(draw_info,x_offset,y_offset,&fill_color,
1715 exception);
1716 fill_opacity=fill_opacity*fill_color.alpha;
1717 CompositePixelOver(image,&fill_color,fill_opacity,q,
1718 GetPixelAlpha(image,q),q);
1719 }
1720 else
1721 {
1722 double
1723 Sa,
1724 Da;
1725
1726 Da=1.0-(QuantumScale*GetPixelAlpha(image,q));
1727 Sa=fill_opacity;
1728 fill_opacity=(1.0-RoundToUnity(Sa+Da-Sa*Da))*QuantumRange;
1729 SetPixelAlpha(image,fill_opacity,q);
1730 }
1731 if (active == MagickFalse)
1732 {
1733 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1734 if (sync == MagickFalse)
1735 status=MagickFalse;
1736 }
1737 q+=GetPixelChannels(image);
1738 }
1739 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1740 if (sync == MagickFalse)
1741 status=MagickFalse;
1742 }
1743 image_view=DestroyCacheView(image_view);
1744 if (((draw_info->stroke.alpha != TransparentAlpha) ||
1745 (draw_info->stroke_pattern != (Image *) NULL)) &&
1746 (status != MagickFalse))
1747 {
1748 /*
1749 Draw text stroke.
1750 */
1751 annotate_info->linejoin=RoundJoin;
1752 annotate_info->affine.tx=offset->x;
1753 annotate_info->affine.ty=offset->y;
1754 (void) ConcatenateString(&annotate_info->primitive,"'");
1755 if (strlen(annotate_info->primitive) > 7)
1756 (void) DrawImage(image,annotate_info,exception);
1757 (void) CloneString(&annotate_info->primitive,"path '");
1758 }
1759 }
1760 if ((fabs(draw_info->interword_spacing) >= MagickEpsilon) &&
1761 (IsUTFSpace(GetUTFCode(p+grapheme[i].cluster)) != MagickFalse) &&
1762 (IsUTFSpace(code) == MagickFalse))
1763 origin.x+=(FT_Pos) (64.0*draw_info->interword_spacing);
1764 else
1765 origin.x+=(FT_Pos) grapheme[i].x_advance;
1766 metrics->origin.x=(double) origin.x;
1767 metrics->origin.y=(double) origin.y;
1768 if (metrics->origin.x > metrics->width)
1769 metrics->width=metrics->origin.x;
1770 if (last_glyph.image != 0)
1771 {
1772 FT_Done_Glyph(last_glyph.image);
1773 last_glyph.image=0;
1774 }
1775 last_glyph=glyph;
1776 code=GetUTFCode(p+grapheme[i].cluster);
1777 }
1778 if (grapheme != (GraphemeInfo *) NULL)
1779 grapheme=(GraphemeInfo *) RelinquishMagickMemory(grapheme);
1780 if (utf8 != (unsigned char *) NULL)
1781 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
1782 if (glyph.image != 0)
1783 {
1784 FT_Done_Glyph(glyph.image);
1785 glyph.image=0;
1786 }
1787 /*
1788 Determine font metrics.
1789 */
1790 metrics->bounds.x1/=64.0;
1791 metrics->bounds.y1/=64.0;
1792 metrics->bounds.x2/=64.0;
1793 metrics->bounds.y2/=64.0;
1794 metrics->origin.x/=64.0;
1795 metrics->origin.y/=64.0;
1796 metrics->width/=64.0;
1797 /*
1798 Relinquish resources.
1799 */
1800 annotate_info=DestroyDrawInfo(annotate_info);
1801 (void) FT_Done_Face(face);
1802 (void) FT_Done_FreeType(library);
1803 return(status);
1804 }
1805 #else
RenderFreetype(Image * image,const DrawInfo * draw_info,const char * magick_unused (encoding),const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)1806 static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1807 const char *magick_unused(encoding),const PointInfo *offset,
1808 TypeMetric *metrics,ExceptionInfo *exception)
1809 {
1810 (void) ThrowMagickException(exception,GetMagickModule(),
1811 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (Freetype)",
1812 draw_info->font != (char *) NULL ? draw_info->font : "none");
1813 return(RenderPostscript(image,draw_info,offset,metrics,exception));
1814 }
1815 #endif
1816
1817 /*
1818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1819 % %
1820 % %
1821 % %
1822 + R e n d e r P o s t s c r i p t %
1823 % %
1824 % %
1825 % %
1826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1827 %
1828 % RenderPostscript() renders text on the image with a Postscript font. It
1829 % also returns the bounding box of the text relative to the image.
1830 %
1831 % The format of the RenderPostscript method is:
1832 %
1833 % MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
1834 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
1835 %
1836 % A description of each parameter follows:
1837 %
1838 % o image: the image.
1839 %
1840 % o draw_info: the draw info.
1841 %
1842 % o offset: (x,y) location of text relative to image.
1843 %
1844 % o metrics: bounding box of text.
1845 %
1846 % o exception: return any errors or warnings in this structure.
1847 %
1848 */
1849
EscapeParenthesis(const char * source)1850 static char *EscapeParenthesis(const char *source)
1851 {
1852 char
1853 *destination;
1854
1855 register char
1856 *q;
1857
1858 register const char
1859 *p;
1860
1861 size_t
1862 length;
1863
1864 assert(source != (const char *) NULL);
1865 length=0;
1866 for (p=source; *p != '\0'; p++)
1867 {
1868 if ((*p == '\\') || (*p == '(') || (*p == ')'))
1869 {
1870 if (~length < 1)
1871 ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
1872 length++;
1873 }
1874 length++;
1875 }
1876 destination=(char *) NULL;
1877 if (~length >= (MagickPathExtent-1))
1878 destination=(char *) AcquireQuantumMemory(length+MagickPathExtent,
1879 sizeof(*destination));
1880 if (destination == (char *) NULL)
1881 ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
1882 *destination='\0';
1883 q=destination;
1884 for (p=source; *p != '\0'; p++)
1885 {
1886 if ((*p == '\\') || (*p == '(') || (*p == ')'))
1887 *q++='\\';
1888 *q++=(*p);
1889 }
1890 *q='\0';
1891 return(destination);
1892 }
1893
RenderPostscript(Image * image,const DrawInfo * draw_info,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)1894 static MagickBooleanType RenderPostscript(Image *image,
1895 const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics,
1896 ExceptionInfo *exception)
1897 {
1898 char
1899 filename[MagickPathExtent],
1900 geometry[MagickPathExtent],
1901 *text;
1902
1903 FILE
1904 *file;
1905
1906 Image
1907 *annotate_image;
1908
1909 ImageInfo
1910 *annotate_info;
1911
1912 int
1913 unique_file;
1914
1915 MagickBooleanType
1916 identity;
1917
1918 PointInfo
1919 extent,
1920 point,
1921 resolution;
1922
1923 register ssize_t
1924 i;
1925
1926 size_t
1927 length;
1928
1929 ssize_t
1930 y;
1931
1932 /*
1933 Render label with a Postscript font.
1934 */
1935 if (image->debug != MagickFalse)
1936 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1937 "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
1938 draw_info->font : "none",draw_info->pointsize);
1939 file=(FILE *) NULL;
1940 unique_file=AcquireUniqueFileResource(filename);
1941 if (unique_file != -1)
1942 file=fdopen(unique_file,"wb");
1943 if ((unique_file == -1) || (file == (FILE *) NULL))
1944 {
1945 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",filename);
1946 return(MagickFalse);
1947 }
1948 (void) FormatLocaleFile(file,"%%!PS-Adobe-3.0\n");
1949 (void) FormatLocaleFile(file,"/ReencodeType\n");
1950 (void) FormatLocaleFile(file,"{\n");
1951 (void) FormatLocaleFile(file," findfont dup length\n");
1952 (void) FormatLocaleFile(file,
1953 " dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
1954 (void) FormatLocaleFile(file,
1955 " /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
1956 (void) FormatLocaleFile(file,"} bind def\n");
1957 /*
1958 Sample to compute bounding box.
1959 */
1960 identity=(fabs(draw_info->affine.sx-draw_info->affine.sy) < MagickEpsilon) &&
1961 (fabs(draw_info->affine.rx) < MagickEpsilon) &&
1962 (fabs(draw_info->affine.ry) < MagickEpsilon) ? MagickTrue : MagickFalse;
1963 extent.x=0.0;
1964 extent.y=0.0;
1965 length=strlen(draw_info->text);
1966 for (i=0; i <= (ssize_t) (length+2); i++)
1967 {
1968 point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
1969 draw_info->affine.ry*2.0*draw_info->pointsize);
1970 point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
1971 draw_info->affine.sy*2.0*draw_info->pointsize);
1972 if (point.x > extent.x)
1973 extent.x=point.x;
1974 if (point.y > extent.y)
1975 extent.y=point.y;
1976 }
1977 (void) FormatLocaleFile(file,"%g %g moveto\n",identity != MagickFalse ? 0.0 :
1978 extent.x/2.0,extent.y/2.0);
1979 (void) FormatLocaleFile(file,"%g %g scale\n",draw_info->pointsize,
1980 draw_info->pointsize);
1981 if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
1982 (strchr(draw_info->font,'/') != (char *) NULL))
1983 (void) FormatLocaleFile(file,
1984 "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
1985 else
1986 (void) FormatLocaleFile(file,
1987 "/%s-ISO dup /%s ReencodeType findfont setfont\n",draw_info->font,
1988 draw_info->font);
1989 (void) FormatLocaleFile(file,"[%g %g %g %g 0 0] concat\n",
1990 draw_info->affine.sx,-draw_info->affine.rx,-draw_info->affine.ry,
1991 draw_info->affine.sy);
1992 text=EscapeParenthesis(draw_info->text);
1993 if (identity == MagickFalse)
1994 (void) FormatLocaleFile(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",
1995 text);
1996 (void) FormatLocaleFile(file,"(%s) show\n",text);
1997 text=DestroyString(text);
1998 (void) FormatLocaleFile(file,"showpage\n");
1999 (void) fclose(file);
2000 (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0!",
2001 floor(extent.x+0.5),floor(extent.y+0.5));
2002 annotate_info=AcquireImageInfo();
2003 (void) FormatLocaleString(annotate_info->filename,MagickPathExtent,"ps:%s",
2004 filename);
2005 (void) CloneString(&annotate_info->page,geometry);
2006 if (draw_info->density != (char *) NULL)
2007 (void) CloneString(&annotate_info->density,draw_info->density);
2008 annotate_info->antialias=draw_info->text_antialias;
2009 annotate_image=ReadImage(annotate_info,exception);
2010 CatchException(exception);
2011 annotate_info=DestroyImageInfo(annotate_info);
2012 (void) RelinquishUniqueFileResource(filename);
2013 if (annotate_image == (Image *) NULL)
2014 return(MagickFalse);
2015 (void) NegateImage(annotate_image,MagickFalse,exception);
2016 resolution.x=DefaultResolution;
2017 resolution.y=DefaultResolution;
2018 if (draw_info->density != (char *) NULL)
2019 {
2020 GeometryInfo
2021 geometry_info;
2022
2023 MagickStatusType
2024 flags;
2025
2026 flags=ParseGeometry(draw_info->density,&geometry_info);
2027 resolution.x=geometry_info.rho;
2028 resolution.y=geometry_info.sigma;
2029 if ((flags & SigmaValue) == 0)
2030 resolution.y=resolution.x;
2031 }
2032 if (identity == MagickFalse)
2033 (void) TransformImage(&annotate_image,"0x0",(char *) NULL,exception);
2034 else
2035 {
2036 RectangleInfo
2037 crop_info;
2038
2039 crop_info=GetImageBoundingBox(annotate_image,exception);
2040 crop_info.height=(size_t) ((resolution.y/DefaultResolution)*
2041 ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
2042 crop_info.y=(ssize_t) ceil((resolution.y/DefaultResolution)*extent.y/8.0-
2043 0.5);
2044 (void) FormatLocaleString(geometry,MagickPathExtent,
2045 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
2046 crop_info.height,(double) crop_info.x,(double) crop_info.y);
2047 (void) TransformImage(&annotate_image,geometry,(char *) NULL,exception);
2048 }
2049 metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
2050 ExpandAffine(&draw_info->affine)*draw_info->pointsize;
2051 metrics->pixels_per_em.y=metrics->pixels_per_em.x;
2052 metrics->ascent=metrics->pixels_per_em.x;
2053 metrics->descent=metrics->pixels_per_em.y/-5.0;
2054 metrics->width=(double) annotate_image->columns/
2055 ExpandAffine(&draw_info->affine);
2056 metrics->height=1.152*metrics->pixels_per_em.x;
2057 metrics->max_advance=metrics->pixels_per_em.x;
2058 metrics->bounds.x1=0.0;
2059 metrics->bounds.y1=metrics->descent;
2060 metrics->bounds.x2=metrics->ascent+metrics->descent;
2061 metrics->bounds.y2=metrics->ascent+metrics->descent;
2062 metrics->underline_position=(-2.0);
2063 metrics->underline_thickness=1.0;
2064 if (draw_info->render == MagickFalse)
2065 {
2066 annotate_image=DestroyImage(annotate_image);
2067 return(MagickTrue);
2068 }
2069 if (draw_info->fill.alpha != TransparentAlpha)
2070 {
2071 CacheView
2072 *annotate_view;
2073
2074 MagickBooleanType
2075 sync;
2076
2077 PixelInfo
2078 fill_color;
2079
2080 /*
2081 Render fill color.
2082 */
2083 if (image->alpha_trait == UndefinedPixelTrait)
2084 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2085 if (annotate_image->alpha_trait == UndefinedPixelTrait)
2086 (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel,
2087 exception);
2088 fill_color=draw_info->fill;
2089 annotate_view=AcquireAuthenticCacheView(annotate_image,exception);
2090 for (y=0; y < (ssize_t) annotate_image->rows; y++)
2091 {
2092 register ssize_t
2093 x;
2094
2095 register Quantum
2096 *magick_restrict q;
2097
2098 q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
2099 1,exception);
2100 if (q == (Quantum *) NULL)
2101 break;
2102 for (x=0; x < (ssize_t) annotate_image->columns; x++)
2103 {
2104 GetFillColor(draw_info,x,y,&fill_color,exception);
2105 SetPixelAlpha(annotate_image,ClampToQuantum((((double) QuantumScale*
2106 GetPixelIntensity(annotate_image,q)*fill_color.alpha))),q);
2107 SetPixelRed(annotate_image,fill_color.red,q);
2108 SetPixelGreen(annotate_image,fill_color.green,q);
2109 SetPixelBlue(annotate_image,fill_color.blue,q);
2110 q+=GetPixelChannels(annotate_image);
2111 }
2112 sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
2113 if (sync == MagickFalse)
2114 break;
2115 }
2116 annotate_view=DestroyCacheView(annotate_view);
2117 (void) CompositeImage(image,annotate_image,OverCompositeOp,MagickTrue,
2118 (ssize_t) ceil(offset->x-0.5),(ssize_t) ceil(offset->y-(metrics->ascent+
2119 metrics->descent)-0.5),exception);
2120 }
2121 annotate_image=DestroyImage(annotate_image);
2122 return(MagickTrue);
2123 }
2124
2125 /*
2126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2127 % %
2128 % %
2129 % %
2130 + R e n d e r X 1 1 %
2131 % %
2132 % %
2133 % %
2134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2135 %
2136 % RenderX11() renders text on the image with an X11 font. It also returns the
2137 % bounding box of the text relative to the image.
2138 %
2139 % The format of the RenderX11 method is:
2140 %
2141 % MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
2142 % const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2143 %
2144 % A description of each parameter follows:
2145 %
2146 % o image: the image.
2147 %
2148 % o draw_info: the draw info.
2149 %
2150 % o offset: (x,y) location of text relative to image.
2151 %
2152 % o metrics: bounding box of text.
2153 %
2154 % o exception: return any errors or warnings in this structure.
2155 %
2156 */
RenderX11(Image * image,const DrawInfo * draw_info,const PointInfo * offset,TypeMetric * metrics,ExceptionInfo * exception)2157 static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
2158 const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2159 {
2160 MagickBooleanType
2161 status;
2162
2163 if (annotate_semaphore == (SemaphoreInfo *) NULL)
2164 ActivateSemaphoreInfo(&annotate_semaphore);
2165 LockSemaphoreInfo(annotate_semaphore);
2166 status=XRenderImage(image,draw_info,offset,metrics,exception);
2167 UnlockSemaphoreInfo(annotate_semaphore);
2168 return(status);
2169 }
2170