MagickCore 7.1.1-43
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
annotate.c
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 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/policy.h"
72#include "MagickCore/property.h"
73#include "MagickCore/resource_.h"
74#include "MagickCore/semaphore.h"
75#include "MagickCore/statistic.h"
76#include "MagickCore/string_.h"
77#include "MagickCore/token.h"
78#include "MagickCore/token-private.h"
79#include "MagickCore/transform.h"
80#include "MagickCore/transform-private.h"
81#include "MagickCore/type.h"
82#include "MagickCore/utility.h"
83#include "MagickCore/utility-private.h"
84#include "MagickCore/xwindow.h"
85#include "MagickCore/xwindow-private.h"
86#include "coders/ghostscript-private.h"
87#if defined(MAGICKCORE_FREETYPE_DELEGATE)
88#if defined(__MINGW32__)
89# undef interface
90#endif
91#include <ft2build.h>
92#if defined(FT_FREETYPE_H)
93# include FT_FREETYPE_H
94#else
95# include <freetype/freetype.h>
96#endif
97#if defined(FT_GLYPH_H)
98# include FT_GLYPH_H
99#else
100# include <freetype/ftglyph.h>
101#endif
102#if defined(FT_OUTLINE_H)
103# include FT_OUTLINE_H
104#else
105# include <freetype/ftoutln.h>
106#endif
107#if defined(FT_BBOX_H)
108# include FT_BBOX_H
109#else
110# include <freetype/ftbbox.h>
111#endif
112#if defined(FT_MODULE_H)
113# include FT_MODULE_H
114#else
115# include <freetype/ftmodapi.h>
116#endif
117#endif
118#if defined(MAGICKCORE_RAQM_DELEGATE)
119#include <raqm.h>
120#endif
121typedef struct _GraphemeInfo
122{
123 ssize_t
124 index,
125 x_offset,
126 x_advance,
127 y_offset,
128 y_advance;
129
130 size_t
131 cluster;
133␌
134/*
135 Annotate semaphores.
136*/
137static SemaphoreInfo
138 *annotate_semaphore = (SemaphoreInfo *) NULL;
139␌
140/*
141 Forward declarations.
142*/
143static MagickBooleanType
144 RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
145 ExceptionInfo *),
146 RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
147 ExceptionInfo *),
148 RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
150 RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *,
151 ExceptionInfo *);
152␌
153/*
154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155% %
156% %
157% %
158+ A n n o t a t e C o m p o n e n t G e n e s i s %
159% %
160% %
161% %
162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163%
164% AnnotateComponentGenesis() instantiates the annotate component.
165%
166% The format of the AnnotateComponentGenesis method is:
167%
168% MagickBooleanType AnnotateComponentGenesis(void)
169%
170*/
171MagickPrivate MagickBooleanType AnnotateComponentGenesis(void)
172{
173 if (annotate_semaphore == (SemaphoreInfo *) NULL)
174 annotate_semaphore=AcquireSemaphoreInfo();
175 return(MagickTrue);
176}
177␌
178/*
179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180% %
181% %
182% %
183+ 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 %
184% %
185% %
186% %
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188%
189% AnnotateComponentTerminus() destroys the annotate component.
190%
191% The format of the AnnotateComponentTerminus method is:
192%
193% AnnotateComponentTerminus(void)
194%
195*/
196MagickPrivate void AnnotateComponentTerminus(void)
197{
198 if (annotate_semaphore == (SemaphoreInfo *) NULL)
199 ActivateSemaphoreInfo(&annotate_semaphore);
200 RelinquishSemaphoreInfo(&annotate_semaphore);
201}
202␌
203/*
204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205% %
206% %
207% %
208% A n n o t a t e I m a g e %
209% %
210% %
211% %
212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
213%
214% AnnotateImage() annotates an image with text.
215%
216% The format of the AnnotateImage method is:
217%
218% MagickBooleanType AnnotateImage(Image *image,DrawInfo *draw_info,
219% ExceptionInfo *exception)
220%
221% A description of each parameter follows:
222%
223% o image: the image.
224%
225% o draw_info: the draw info.
226%
227% o exception: return any errors or warnings in this structure.
228%
229*/
230MagickExport MagickBooleanType AnnotateImage(Image *image,
231 const DrawInfo *draw_info,ExceptionInfo *exception)
232{
233 char
234 *p,
235 color[MagickPathExtent],
236 primitive[MagickPathExtent],
237 *text,
238 **textlist;
239
241 *annotate,
242 *annotate_info;
243
245 geometry_info;
246
247 MagickBooleanType
248 status;
249
251 pixel;
252
254 offset;
255
257 geometry;
258
259 ssize_t
260 i;
261
263 metrics;
264
265 size_t
266 height,
267 number_lines;
268
269 assert(image != (Image *) NULL);
270 assert(image->signature == MagickCoreSignature);
271 if (IsEventLogging() != MagickFalse)
272 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
273 assert(draw_info != (DrawInfo *) NULL);
274 assert(draw_info->signature == MagickCoreSignature);
275 if (draw_info->text == (char *) NULL)
276 return(MagickFalse);
277 if (*draw_info->text == '\0')
278 return(MagickTrue);
279 annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
280 text=annotate->text;
281 annotate->text=(char *) NULL;
282 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
283 number_lines=1;
284 for (p=text; *p != '\0'; p++)
285 if (*p == '\n')
286 number_lines++;
287 textlist=(char **) AcquireQuantumMemory(number_lines+1,sizeof(*textlist));
288 if (textlist == (char **) NULL)
289 {
290 annotate_info=DestroyDrawInfo(annotate_info);
291 annotate=DestroyDrawInfo(annotate);
292 text=DestroyString(text);
293 return(MagickFalse);
294 }
295 p=text;
296 for (i=0; i < (ssize_t) number_lines; i++)
297 {
298 char
299 *q;
300
301 textlist[i]=p;
302 for (q=p; *q != '\0'; q++)
303 if ((*q == '\r') || (*q == '\n'))
304 break;
305 if (*q == '\r')
306 {
307 *q='\0';
308 q++;
309 }
310 *q='\0';
311 p=q+1;
312 }
313 textlist[i]=(char *) NULL;
314 SetGeometry(image,&geometry);
315 SetGeometryInfo(&geometry_info);
316 if (annotate_info->geometry != (char *) NULL)
317 {
318 (void) ParsePageGeometry(image,annotate_info->geometry,&geometry,
319 exception);
320 (void) ParseGeometry(annotate_info->geometry,&geometry_info);
321 }
322 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
323 {
324 annotate_info=DestroyDrawInfo(annotate_info);
325 annotate=DestroyDrawInfo(annotate);
326 textlist=(char **) RelinquishMagickMemory(textlist);
327 text=DestroyString(text);
328 return(MagickFalse);
329 }
330 if (IsGrayColorspace(image->colorspace) != MagickFalse)
331 (void) SetImageColorspace(image,sRGBColorspace,exception);
332 status=MagickTrue;
333 (void) memset(&metrics,0,sizeof(metrics));
334 for (i=0; textlist[i] != (char *) NULL; i++)
335 {
336 if (*textlist[i] == '\0')
337 continue;
338 /*
339 Position text relative to image.
340 */
341 annotate_info->affine.tx=geometry_info.xi-image->page.x;
342 annotate_info->affine.ty=geometry_info.psi-image->page.y;
343 (void) CloneString(&annotate->text,textlist[i]);
344 if ((metrics.width == 0) || (annotate->gravity != NorthWestGravity))
345 (void) GetTypeMetrics(image,annotate,&metrics,exception);
346 height=CastDoubleToUnsigned(metrics.ascent-metrics.descent+0.5);
347 if (height == 0)
348 height=draw_info->pointsize;
349 height=CastDoubleToUnsigned(floor((double) height+
350 draw_info->interline_spacing+0.5));
351 switch (annotate->gravity)
352 {
353 case UndefinedGravity:
354 default:
355 {
356 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
357 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
358 break;
359 }
360 case (GravityType) NorthWestGravity:
361 {
362 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
363 annotate_info->affine.ry*height+annotate_info->affine.ry*
364 (metrics.ascent+metrics.descent);
365 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
366 annotate_info->affine.sy*height+annotate_info->affine.sy*
367 metrics.ascent;
368 break;
369 }
370 case (GravityType) NorthGravity:
371 {
372 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
373 geometry.width/2.0+i*annotate_info->affine.ry*height-
374 annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
375 (metrics.ascent+metrics.descent);
376 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
377 annotate_info->affine.sy*height+annotate_info->affine.sy*
378 metrics.ascent-annotate_info->affine.rx*metrics.width/2.0;
379 break;
380 }
381 case (GravityType) NorthEastGravity:
382 {
383 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
384 geometry.width+i*annotate_info->affine.ry*height-
385 annotate_info->affine.sx*metrics.width+annotate_info->affine.ry*
386 (metrics.ascent+metrics.descent)-1.0;
387 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
388 annotate_info->affine.sy*height+annotate_info->affine.sy*
389 metrics.ascent-annotate_info->affine.rx*metrics.width;
390 break;
391 }
392 case (GravityType) WestGravity:
393 {
394 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
395 annotate_info->affine.ry*height+annotate_info->affine.ry*
396 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
397 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
398 geometry.height/2.0+i*annotate_info->affine.sy*height+
399 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
400 (number_lines-1.0)*height)/2.0;
401 break;
402 }
403 case (GravityType) CenterGravity:
404 {
405 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
406 geometry.width/2.0+i*annotate_info->affine.ry*height-
407 annotate_info->affine.sx*metrics.width/2.0+annotate_info->affine.ry*
408 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
409 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
410 geometry.height/2.0+i*annotate_info->affine.sy*height-
411 annotate_info->affine.rx*metrics.width/2.0+annotate_info->affine.sy*
412 (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
413 break;
414 }
415 case (GravityType) EastGravity:
416 {
417 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
418 geometry.width+i*annotate_info->affine.ry*height-
419 annotate_info->affine.sx*metrics.width+
420 annotate_info->affine.ry*(metrics.ascent+metrics.descent-
421 (number_lines-1.0)*height)/2.0-1.0;
422 offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
423 geometry.height/2.0+i*annotate_info->affine.sy*height-
424 annotate_info->affine.rx*metrics.width+
425 annotate_info->affine.sy*(metrics.ascent+metrics.descent-
426 (number_lines-1.0)*height)/2.0;
427 break;
428 }
429 case (GravityType) SouthWestGravity:
430 {
431 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
432 annotate_info->affine.ry*height-annotate_info->affine.ry*
433 (number_lines-1.0)*height;
434 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
435 geometry.height+i*annotate_info->affine.sy*height-
436 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
437 break;
438 }
439 case (GravityType) SouthGravity:
440 {
441 offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
442 geometry.width/2.0+i*annotate_info->affine.ry*height-
443 annotate_info->affine.sx*metrics.width/2.0-
444 annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
445 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
446 geometry.height+i*annotate_info->affine.sy*height-
447 annotate_info->affine.rx*metrics.width/2.0-
448 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
449 break;
450 }
451 case (GravityType) SouthEastGravity:
452 {
453 offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
454 geometry.width+i*annotate_info->affine.ry*height-
455 annotate_info->affine.sx*metrics.width-
456 annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
457 offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
458 geometry.height+i*annotate_info->affine.sy*height-
459 annotate_info->affine.rx*metrics.width-
460 annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
461 break;
462 }
463 }
464 switch (annotate->align)
465 {
466 case LeftAlign:
467 {
468 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
469 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
470 break;
471 }
472 case CenterAlign:
473 {
474 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
475 annotate_info->affine.sx*metrics.width/2.0;
476 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
477 annotate_info->affine.rx*metrics.width/2.0;
478 break;
479 }
480 case RightAlign:
481 {
482 offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
483 annotate_info->affine.sx*metrics.width;
484 offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
485 annotate_info->affine.rx*metrics.width;
486 break;
487 }
488 default:
489 break;
490 }
491 if (draw_info->undercolor.alpha != (MagickRealType) TransparentAlpha)
492 {
494 *undercolor_info;
495
496 /*
497 Text box.
498 */
499 undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
500 undercolor_info->fill=draw_info->undercolor;
501 undercolor_info->affine=draw_info->affine;
502 undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
503 undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
504 (void) FormatLocaleString(primitive,MagickPathExtent,
505 "rectangle 0.0,0.0 %g,%g",metrics.origin.x,(double) height);
506 (void) CloneString(&undercolor_info->primitive,primitive);
507 (void) DrawImage(image,undercolor_info,exception);
508 (void) DestroyDrawInfo(undercolor_info);
509 }
510 annotate_info->affine.tx=offset.x;
511 annotate_info->affine.ty=offset.y;
512 pixel=annotate_info->fill;
513 if (annotate_info->stroke.alpha != (MagickRealType) TransparentAlpha)
514 pixel=annotate_info->stroke;
515 (void) QueryColorname(image,&pixel,AllCompliance,color,exception);
516 (void) FormatLocaleString(primitive,MagickPathExtent,"stroke %s "
517 "stroke-width %g line 0,0 %g,0",color,(double)
518 metrics.underline_thickness,(double) metrics.width);
519 /*
520 Annotate image with text.
521 */
522 switch (annotate->decorate)
523 {
524 case OverlineDecoration:
525 {
526 annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
527 metrics.descent-metrics.underline_position));
528 (void) CloneString(&annotate_info->primitive,primitive);
529 (void) DrawImage(image,annotate_info,exception);
530 break;
531 }
532 case UnderlineDecoration:
533 {
534 annotate_info->affine.ty-=(draw_info->affine.sy*
535 metrics.underline_position);
536 (void) CloneString(&annotate_info->primitive,primitive);
537 (void) DrawImage(image,annotate_info,exception);
538 break;
539 }
540 default:
541 break;
542 }
543 status=RenderType(image,annotate,&offset,&metrics,exception);
544 if (status == MagickFalse)
545 break;
546
547 if (annotate->decorate == LineThroughDecoration) {
548 annotate_info->affine.ty-=(draw_info->affine.sy*(height+
549 metrics.underline_position+metrics.descent*2)/2.0);
550 (void) CloneString(&annotate_info->primitive,primitive);
551 (void) DrawImage(image,annotate_info,exception);
552 }
553 }
554 /*
555 Relinquish resources.
556 */
557 annotate_info=DestroyDrawInfo(annotate_info);
558 annotate=DestroyDrawInfo(annotate);
559 textlist=(char **) RelinquishMagickMemory(textlist);
560 text=DestroyString(text);
561 return(status);
562}
563␌
564/*
565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566% %
567% %
568% %
569% F o r m a t M a g i c k C a p t i o n %
570% %
571% %
572% %
573%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
574%
575% FormatMagickCaption() formats a caption so that it fits within the image
576% width. It returns the number of lines in the formatted caption.
577%
578% The format of the FormatMagickCaption method is:
579%
580% ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
581% const MagickBooleanType split,TypeMetric *metrics,char **caption,
582% ExceptionInfo *exception)
583%
584% A description of each parameter follows.
585%
586% o image: The image.
587%
588% o draw_info: the draw info.
589%
590% o split: when no convenient line breaks-- insert newline.
591%
592% o metrics: Return the font metrics in this structure.
593%
594% o caption: the caption.
595%
596% o exception: return any errors or warnings in this structure.
597%
598*/
599
600static inline char *ReplaceSpaceWithNewline(char **caption,char *space)
601{
602 size_t
603 octets;
604
605 if ((caption == (char **) NULL) || (space == (char *) NULL))
606 return((char *) NULL);
607 octets=(size_t) GetUTFOctets(space);
608 if (octets == 1)
609 *space='\n';
610 else
611 {
612 char
613 *target;
614
615 size_t
616 length;
617
618 ssize_t
619 offset;
620
621 length=strlen(*caption);
622 *space='\n';
623 offset=space-(*caption);
624 if (offset >= 0)
625 {
626 target=AcquireString(*caption);
627 CopyMagickString(target,*caption,(size_t) offset+2);
628 ConcatenateMagickString(target,space+octets,length);
629 (void) DestroyString(*caption);
630 *caption=target;
631 space=(*caption)+offset;
632 }
633 }
634 return(space);
635}
636
637MagickExport ssize_t FormatMagickCaption(Image *image,DrawInfo *draw_info,
638 const MagickBooleanType split,TypeMetric *metrics,char **caption,
639 ExceptionInfo *exception)
640{
641 char
642 *p,
643 *q,
644 *s;
645
646 MagickBooleanType
647 status;
648
649 size_t
650 width;
651
652 ssize_t
653 i,
654 n;
655
656 q=draw_info->text;
657 s=(char *) NULL;
658 width=0;
659 for (p=(*caption); GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p))
660 {
661 int
662 code;
663
664 code=GetUTFCode(p);
665 if (code == '\n')
666 {
667 q=draw_info->text;
668 continue;
669 }
670 if ((draw_info->word_break != BreakWordBreakType) &&
671 (IsUTFSpace(code) != MagickFalse) &&
672 (IsNonBreakingUTFSpace(code) == MagickFalse))
673 {
674 s=p;
675 if (width > image->columns)
676 {
677 p=ReplaceSpaceWithNewline(caption,s);
678 s=p;
679 }
680 }
681 for (i=0; i < (ssize_t) GetUTFOctets(p); i++)
682 *q++=(*(p+i));
683 *q='\0';
684 status=GetTypeMetrics(image,draw_info,metrics,exception);
685 if (status == MagickFalse)
686 break;
687 width=CastDoubleToUnsigned(metrics->width+draw_info->stroke_width+0.5);
688 if (width <= image->columns)
689 continue;
690 if (s != (char *) NULL)
691 p=ReplaceSpaceWithNewline(caption,s);
692 else
693 if ((split != MagickFalse) || (GetUTFOctets(p) > 2))
694 {
695 /*
696 No convenient line breaks-- insert newline.
697 */
698 n=p-(*caption);
699 if ((n > 0) && ((*caption)[n-1] != '\n'))
700 {
701 char
702 *target;
703
704 target=AcquireString(*caption);
705 CopyMagickString(target,*caption,(size_t) n+1);
706 ConcatenateMagickString(target,"\n",strlen(*caption)+1);
707 ConcatenateMagickString(target,p,strlen(*caption)+2);
708 (void) DestroyString(*caption);
709 *caption=target;
710 p=(*caption)+n;
711 }
712 }
713 q=draw_info->text;
714 s=(char *) NULL;
715 }
716 n=0;
717 for (p=(*caption); GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p))
718 if (GetUTFCode(p) == '\n')
719 n++;
720 return(n);
721}
722␌
723/*
724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725% %
726% %
727% %
728% G e t M u l t i l i n e T y p e M e t r i c s %
729% %
730% %
731% %
732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733%
734% GetMultilineTypeMetrics() returns the following information for the
735% specified font and text:
736%
737% character width
738% character height
739% ascender
740% descender
741% text width
742% text height
743% maximum horizontal advance
744% bounds: x1
745% bounds: y1
746% bounds: x2
747% bounds: y2
748% origin: x
749% origin: y
750% underline position
751% underline thickness
752%
753% This method is like GetTypeMetrics() but it returns the maximum text width
754% and height for multiple lines of text.
755%
756% The format of the GetMultilineTypeMetrics method is:
757%
758% MagickBooleanType GetMultilineTypeMetrics(Image *image,
759% const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
760%
761% A description of each parameter follows:
762%
763% o image: the image.
764%
765% o draw_info: the draw info.
766%
767% o metrics: Return the font metrics in this structure.
768%
769% o exception: return any errors or warnings in this structure.
770%
771*/
772MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
773 const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
774{
775 char
776 **textlist;
777
778 double
779 height;
780
782 *annotate_info;
783
784 MagickBooleanType
785 status;
786
787 MagickSizeType
788 size;
789
790 ssize_t
791 i;
792
793 size_t
794 count;
795
797 extent;
798
799 assert(image != (Image *) NULL);
800 assert(image->signature == MagickCoreSignature);
801 if (IsEventLogging() != MagickFalse)
802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
803 assert(draw_info != (DrawInfo *) NULL);
804 assert(draw_info->text != (char *) NULL);
805 assert(draw_info->signature == MagickCoreSignature);
806 if (*draw_info->text == '\0')
807 {
808 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
809 "LabelExpected","`%s'",image->filename);
810 return(MagickFalse);
811 }
812 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
813 annotate_info->text=DestroyString(annotate_info->text);
814 /*
815 Convert newlines to multiple lines of text.
816 */
817 textlist=StringToStrings(draw_info->text,&count);
818 if (textlist == (char **) NULL)
819 {
820 annotate_info=DestroyDrawInfo(annotate_info);
821 return(MagickFalse);
822 }
823 annotate_info->render=MagickFalse;
824 annotate_info->direction=UndefinedDirection;
825 (void) memset(metrics,0,sizeof(*metrics));
826 (void) memset(&extent,0,sizeof(extent));
827 /*
828 Find the widest of the text lines.
829 */
830 annotate_info->text=textlist[0];
831 status=GetTypeMetrics(image,annotate_info,&extent,exception);
832 *metrics=extent;
833 height=(count*(size_t) (metrics->ascent-metrics->descent+
834 0.5)+(count-1)*draw_info->interline_spacing);
835 size=(MagickSizeType) fabs(height);
836 if (AcquireMagickResource(HeightResource,size) == MagickFalse)
837 {
838 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
839 "WidthOrHeightExceedsLimit","`%s'",image->filename);
840 status=MagickFalse;
841 }
842 else
843 {
844 for (i=1; i < (ssize_t) count; i++)
845 {
846 annotate_info->text=textlist[i];
847 status=GetTypeMetrics(image,annotate_info,&extent,exception);
848 if (status == MagickFalse)
849 break;
850 if (extent.width > metrics->width)
851 *metrics=extent;
852 size=(MagickSizeType) fabs(extent.width);
853 if (AcquireMagickResource(WidthResource,size) == MagickFalse)
854 {
855 (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
856 "WidthOrHeightExceedsLimit","`%s'",image->filename);
857 status=MagickFalse;
858 break;
859 }
860 }
861 metrics->height=(double) height;
862 }
863 /*
864 Relinquish resources.
865 */
866 annotate_info->text=(char *) NULL;
867 annotate_info=DestroyDrawInfo(annotate_info);
868 for (i=0; i < (ssize_t) count; i++)
869 textlist[i]=DestroyString(textlist[i]);
870 textlist=(char **) RelinquishMagickMemory(textlist);
871 return(status);
872}
873␌
874/*
875%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
876% %
877% %
878% %
879% G e t T y p e M e t r i c s %
880% %
881% %
882% %
883%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
884%
885% GetTypeMetrics() returns the following information for the specified font
886% and text:
887%
888% character width
889% character height
890% ascender
891% descender
892% text width
893% text height
894% maximum horizontal advance
895% bounds: x1
896% bounds: y1
897% bounds: x2
898% bounds: y2
899% origin: x
900% origin: y
901% underline position
902% underline thickness
903%
904% The format of the GetTypeMetrics method is:
905%
906% MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
907% 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 metrics: Return the font metrics in this structure.
916%
917% o exception: return any errors or warnings in this structure.
918%
919*/
920MagickExport MagickBooleanType GetTypeMetrics(Image *image,
921 const DrawInfo *draw_info,TypeMetric *metrics,ExceptionInfo *exception)
922{
924 *annotate_info;
925
926 MagickBooleanType
927 status;
928
930 offset;
931
932 assert(image != (Image *) NULL);
933 assert(image->signature == MagickCoreSignature);
934 if (IsEventLogging() != MagickFalse)
935 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
936 assert(draw_info != (DrawInfo *) NULL);
937 assert(draw_info->text != (char *) NULL);
938 assert(draw_info->signature == MagickCoreSignature);
939 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
940 annotate_info->render=MagickFalse;
941 annotate_info->direction=UndefinedDirection;
942 (void) memset(metrics,0,sizeof(*metrics));
943 offset.x=0.0;
944 offset.y=0.0;
945 status=RenderType(image,annotate_info,&offset,metrics,exception);
946 if (draw_info->debug != MagickFalse)
947 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
948 "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
949 "bounds: %g,%g %g,%g; origin: %g,%g; pixels per em: %g,%g; "
950 "underline position: %g; underline thickness: %g",annotate_info->text,
951 metrics->width,metrics->height,metrics->ascent,metrics->descent,
952 metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
953 metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
954 metrics->pixels_per_em.x,metrics->pixels_per_em.y,
955 metrics->underline_position,metrics->underline_thickness);
956 annotate_info=DestroyDrawInfo(annotate_info);
957 return(status);
958}
959␌
960/*
961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962% %
963% %
964% %
965+ R e n d e r T y p e %
966% %
967% %
968% %
969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970%
971% RenderType() renders text on the image. It also returns the bounding box of
972% the text relative to the image.
973%
974% The format of the RenderType method is:
975%
976% MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
977% const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
978%
979% A description of each parameter follows:
980%
981% o image: the image.
982%
983% o draw_info: the draw info.
984%
985% o offset: (x,y) location of text relative to image.
986%
987% o metrics: bounding box of text.
988%
989% o exception: return any errors or warnings in this structure.
990%
991*/
992static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
993 const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
994{
995 char
996 *font;
997
998 const TypeInfo
999 *type_info;
1000
1001 DrawInfo
1002 *annotate_info;
1003
1004 MagickBooleanType
1005 status;
1006
1007 type_info=(const TypeInfo *) NULL;
1008 if (draw_info->font != (char *) NULL)
1009 {
1010 if (*draw_info->font == '@')
1011 {
1012 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
1013 metrics,exception);
1014 return(status);
1015 }
1016 if (*draw_info->font == '-')
1017 return(RenderX11(image,draw_info,offset,metrics,exception));
1018 if (*draw_info->font == '^')
1019 return(RenderPostscript(image,draw_info,offset,metrics,exception));
1020 if (IsPathAccessible(draw_info->font) != MagickFalse)
1021 {
1022 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
1023 metrics,exception);
1024 return(status);
1025 }
1026 type_info=GetTypeInfo(draw_info->font,exception);
1027 if (type_info == (const TypeInfo *) NULL)
1028 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1029 "UnableToReadFont","`%s'",draw_info->font);
1030 }
1031 if ((type_info == (const TypeInfo *) NULL) &&
1032 (draw_info->family != (const char *) NULL))
1033 {
1034 if (strpbrk(draw_info->family,",'\"") == (char *) NULL)
1035 type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
1036 draw_info->stretch,draw_info->weight,exception);
1037 if (type_info == (const TypeInfo *) NULL)
1038 {
1039 char
1040 **family;
1041
1042 int
1043 number_families;
1044
1045 ssize_t
1046 i;
1047
1048 /*
1049 Parse font family list.
1050 */
1051 family=StringToArgv(draw_info->family,&number_families);
1052 for (i=1; i < (ssize_t) number_families; i++)
1053 {
1054 (void) SubstituteString(&family[i],",","");
1055 type_info=GetTypeInfoByFamily(family[i],draw_info->style,
1056 draw_info->stretch,draw_info->weight,exception);
1057 if ((type_info != (const TypeInfo *) NULL) &&
1058 (LocaleCompare(family[i],type_info->family) == 0))
1059 break;
1060 }
1061 for (i=0; i < (ssize_t) number_families; i++)
1062 family[i]=DestroyString(family[i]);
1063 family=(char **) RelinquishMagickMemory(family);
1064 if (type_info == (const TypeInfo *) NULL)
1065 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1066 "UnableToReadFont","`%s'",draw_info->family);
1067 }
1068 }
1069 font=GetPolicyValue("system:font");
1070 if (font != (char *) NULL)
1071 {
1072 if (IsPathAccessible(font) != MagickFalse)
1073 {
1074 /*
1075 Render with default system font.
1076 */
1077 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1078 annotate_info->font=font;
1079 status=RenderFreetype(image,annotate_info,annotate_info->encoding,
1080 offset,metrics,exception);
1081 annotate_info=DestroyDrawInfo(annotate_info);
1082 return(status);
1083 }
1084 font=DestroyString(font);
1085 }
1086 if (type_info == (const TypeInfo *) NULL)
1087 {
1089 *sans_exception;
1090
1091 /*
1092 Search for a default font.
1093 */
1094 sans_exception=AcquireExceptionInfo();
1095 if (type_info == (const TypeInfo *) NULL)
1096 type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
1097 draw_info->stretch,draw_info->weight,sans_exception);
1098 if (type_info == (const TypeInfo *) NULL)
1099 type_info=GetTypeInfo("*",sans_exception);
1100 sans_exception=DestroyExceptionInfo(sans_exception);
1101 }
1102 if (type_info == (const TypeInfo *) NULL)
1103 {
1104 status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics,
1105 exception);
1106 return(status);
1107 }
1108 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1109 annotate_info->face=type_info->face;
1110 if (type_info->metrics != (char *) NULL)
1111 (void) CloneString(&annotate_info->metrics,type_info->metrics);
1112 if (type_info->glyphs != (char *) NULL)
1113 (void) CloneString(&annotate_info->font,type_info->glyphs);
1114 status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics,
1115 exception);
1116 annotate_info=DestroyDrawInfo(annotate_info);
1117 return(status);
1118}
1119␌
1120/*
1121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122% %
1123% %
1124% %
1125+ R e n d e r F r e e t y p e %
1126% %
1127% %
1128% %
1129%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1130%
1131% RenderFreetype() renders text on the image with a Truetype font. It also
1132% returns the bounding box of the text relative to the image.
1133%
1134% The format of the RenderFreetype method is:
1135%
1136% MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
1137% const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1138% ExceptionInfo *exception)
1139%
1140% A description of each parameter follows:
1141%
1142% o image: the image.
1143%
1144% o draw_info: the draw info.
1145%
1146% o encoding: the font encoding.
1147%
1148% o offset: (x,y) location of text relative to image.
1149%
1150% o metrics: bounding box of text.
1151%
1152% o exception: return any errors or warnings in this structure.
1153%
1154*/
1155
1156#if defined(MAGICKCORE_FREETYPE_DELEGATE)
1157
1158#if defined(MAGICKCORE_RAQM_DELEGATE)
1159static size_t ComplexRaqmTextLayout(const Image *image,
1160 const DrawInfo *draw_info,const char *text,const size_t length,
1161 const FT_Face face,GraphemeInfo **grapheme,ExceptionInfo *exception)
1162{
1163 const char
1164 *features;
1165
1166 raqm_t
1167 *rq;
1168
1169 raqm_glyph_t
1170 *glyphs;
1171
1172 size_t
1173 extent;
1174
1175 ssize_t
1176 i;
1177
1178 extent=0;
1179 rq=raqm_create();
1180 if (rq == (raqm_t *) NULL)
1181 goto cleanup;
1182 if (raqm_set_text_utf8(rq,text,length) == 0)
1183 goto cleanup;
1184 if (raqm_set_par_direction(rq,(raqm_direction_t) draw_info->direction) == 0)
1185 goto cleanup;
1186 if (raqm_set_freetype_face(rq,face) == 0)
1187 goto cleanup;
1188 features=GetImageProperty(image,"type:features",exception);
1189 if (features != (const char *) NULL)
1190 {
1191 char
1192 breaker,
1193 quote,
1194 *token;
1195
1196 int
1197 next,
1198 status_token;
1199
1200 TokenInfo
1201 *token_info;
1202
1203 next=0;
1204 token_info=AcquireTokenInfo();
1205 token=AcquireString("");
1206 status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1207 &breaker,&next,&quote);
1208 while (status_token == 0)
1209 {
1210 raqm_add_font_feature(rq,token,(int) strlen(token));
1211 status_token=Tokenizer(token_info,0,token,50,features,"",",","",'\0',
1212 &breaker,&next,&quote);
1213 }
1214 token_info=DestroyTokenInfo(token_info);
1215 token=DestroyString(token);
1216 }
1217 if (raqm_layout(rq) == 0)
1218 goto cleanup;
1219 glyphs=raqm_get_glyphs(rq,&extent);
1220 if (glyphs == (raqm_glyph_t *) NULL)
1221 {
1222 extent=0;
1223 goto cleanup;
1224 }
1225 *grapheme=(GraphemeInfo *) AcquireQuantumMemory(extent,sizeof(**grapheme));
1226 if (*grapheme == (GraphemeInfo *) NULL)
1227 {
1228 extent=0;
1229 goto cleanup;
1230 }
1231 for (i=0; i < (ssize_t) extent; i++)
1232 {
1233 (*grapheme)[i].index=glyphs[i].index;
1234 (*grapheme)[i].x_offset=glyphs[i].x_offset;
1235 (*grapheme)[i].x_advance=glyphs[i].x_advance;
1236 (*grapheme)[i].y_offset=glyphs[i].y_offset;
1237 (*grapheme)[i].y_advance=glyphs[i].y_advance;
1238 (*grapheme)[i].cluster=glyphs[i].cluster;
1239 }
1240
1241cleanup:
1242 raqm_destroy(rq);
1243 return(extent);
1244#else
1245static size_t ComplexTextLayout(const DrawInfo *draw_info,const char *text,
1246 const size_t length,const FT_Face face,const FT_Int32 flags,
1247 GraphemeInfo **grapheme)
1248{
1249 const char
1250 *p;
1251
1252 ssize_t
1253 i;
1254
1255 ssize_t
1256 last_glyph;
1257
1258 /*
1259 Simple layout for bi-directional text (right-to-left or left-to-right).
1260 */
1261 *grapheme=(GraphemeInfo *) AcquireQuantumMemory(length+1,sizeof(**grapheme));
1262 if (*grapheme == (GraphemeInfo *) NULL)
1263 return(0);
1264 last_glyph=0;
1265 p=text;
1266 for (i=0; GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p), i++)
1267 {
1268 (*grapheme)[i].index=(ssize_t) FT_Get_Char_Index(face,(FT_ULong)
1269 GetUTFCode(p));
1270 (*grapheme)[i].x_offset=0;
1271 (*grapheme)[i].y_offset=0;
1272 if (((*grapheme)[i].index != 0) && (last_glyph != 0))
1273 {
1274 if (FT_HAS_KERNING(face))
1275 {
1276 FT_Error
1277 ft_status;
1278
1279 FT_Vector
1280 kerning;
1281
1282 ft_status=FT_Get_Kerning(face,(FT_UInt) last_glyph,(FT_UInt)
1283 (*grapheme)[i].index,ft_kerning_default,&kerning);
1284 if (ft_status == 0)
1285 (*grapheme)[i-1].x_advance+=(FT_Pos) ((draw_info->direction ==
1286 RightToLeftDirection ? -1.0 : 1.0)*kerning.x);
1287 }
1288 }
1289 (void) FT_Load_Glyph(face,(FT_UInt) (*grapheme)[i].index,flags);
1290 (*grapheme)[i].x_advance=face->glyph->advance.x;
1291 (*grapheme)[i].y_advance=face->glyph->advance.y;
1292 (*grapheme)[i].cluster=(size_t) (p-text);
1293 last_glyph=(*grapheme)[i].index;
1294 }
1295 return((size_t) i);
1296#endif
1297}
1298
1299static void FreetypeCloseStream(FT_Stream stream)
1300{
1301 FILE
1302 *file;
1303
1304 file=(FILE *) stream->descriptor.pointer;
1305 if (file != (FILE *) NULL)
1306 (void) fclose(file);
1307 stream->descriptor.pointer=NULL;
1308}
1309
1310static unsigned long FreetypeReadStream(FT_Stream stream,unsigned long offset,
1311 unsigned char *buffer,unsigned long count)
1312{
1313 FILE
1314 *file;
1315
1316 unsigned long
1317 result;
1318
1319 file=(FILE *) stream->descriptor.pointer;
1320 if (file == (FILE *) NULL)
1321 return(0);
1322 if (offset > stream->size)
1323 result=1;
1324 else
1325 result=(unsigned long) fseek(file,(off_t) offset,SEEK_SET);
1326 if (count == 0) /* seek operation */
1327 return(result);
1328 if (result != 0)
1329 return(0);
1330 return((unsigned long) fread(buffer,1,count,file));
1331}
1332
1333static inline MagickBooleanType IsEmptyOutline(FT_Outline outline)
1334{
1335 return((outline.n_points == 0) || (outline.n_contours <= 0) ? MagickTrue :
1336 MagickFalse);
1337}
1338
1339static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
1340 DrawInfo *draw_info)
1341{
1343 affine;
1344
1345 char
1346 path[MagickPathExtent];
1347
1348 affine=draw_info->affine;
1349 (void) FormatLocaleString(path,MagickPathExtent,"C%g,%g %g,%g %g,%g",
1350 affine.tx+p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,affine.ty-
1351 q->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1352 (void) ConcatenateString(&draw_info->primitive,path);
1353 return(0);
1354}
1355
1356static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
1357{
1359 affine;
1360
1361 char
1362 path[MagickPathExtent];
1363
1364 affine=draw_info->affine;
1365 (void) FormatLocaleString(path,MagickPathExtent,"L%g,%g",affine.tx+to->x/64.0,
1366 affine.ty-to->y/64.0);
1367 (void) ConcatenateString(&draw_info->primitive,path);
1368 return(0);
1369}
1370
1371static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
1372{
1374 affine;
1375
1376 char
1377 path[MagickPathExtent];
1378
1379 affine=draw_info->affine;
1380 (void) FormatLocaleString(path,MagickPathExtent,"M%g,%g",affine.tx+to->x/64.0,
1381 affine.ty-to->y/64.0);
1382 (void) ConcatenateString(&draw_info->primitive,path);
1383 return(0);
1384}
1385
1386static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
1387 DrawInfo *draw_info)
1388{
1390 affine;
1391
1392 char
1393 path[MagickPathExtent];
1394
1395 affine=draw_info->affine;
1396 (void) FormatLocaleString(path,MagickPathExtent,"Q%g,%g %g,%g",affine.tx+
1397 control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,affine.ty-
1398 to->y/64.0);
1399 (void) ConcatenateString(&draw_info->primitive,path);
1400 return(0);
1401}
1402
1403#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 10
1404static inline const char *FreetypeErrorMessage(FT_Error ft_status)
1405{
1406 return(FT_Error_String(ft_status));
1407#else
1408static inline const char *FreetypeErrorMessage(
1409 FT_Error magick_unused(ft_status))
1410{
1411 magick_unreferenced(ft_status);
1412 return((const char *) NULL);
1413#endif
1414}
1415
1416static void *FreetypeAlloc(FT_Memory magick_unused(memory),long size)
1417{
1418 magick_unreferenced(memory);
1419 if (size < 0)
1420 return((void *) NULL);
1421 if ((size_t) size > GetMaxMemoryRequest())
1422 return((void *) NULL);
1423 return(AcquireMagickMemory((size_t) size));
1424}
1425
1426static void *FreetypeRealloc(FT_Memory magick_unused(memory),
1427 long magick_unused(cur_size),long size,void *block)
1428{
1429 magick_unreferenced(memory);
1430 magick_unreferenced(cur_size);
1431 if (size < 0)
1432 return((void *) NULL);
1433 if ((size_t) size > GetMaxMemoryRequest())
1434 return((void *) NULL);
1435 return(ResizeMagickMemory(block,(size_t) size));
1436}
1437
1438static void FreetypeFree(FT_Memory magick_unused(memory),void *block)
1439{
1440 magick_unreferenced(memory);
1441 (void) RelinquishMagickMemory(block);
1442}
1443
1444static FT_Memory FreetypeAcquireMemoryManager(void)
1445{
1446 FT_Memory
1447 memory;
1448
1449 memory=(FT_Memory) AcquireMagickMemory(sizeof(*memory));
1450 if (memory == (FT_Memory) NULL)
1451 return(memory);
1452 memset(memory,0,sizeof(*memory));
1453 memory->alloc=(&FreetypeAlloc);
1454 memory->realloc=(&FreetypeRealloc);
1455 memory->free=(&FreetypeFree);
1456 return(memory);
1457}
1458
1459static void FreetypeDone(FT_Memory memory,FT_Library library,
1460 FT_StreamRec *stream)
1461{
1462 (void) FT_Done_Library(library);
1463 stream=(FT_StreamRec *) RelinquishMagickMemory(stream);
1464 memory=(FT_Memory) RelinquishMagickMemory(memory);
1465}
1466
1467static FT_Error FreetypeInit(FT_Memory memory,FT_Library *alibrary)
1468{
1469 FT_Error
1470 ft_status;
1471
1472 ft_status=FT_New_Library(memory,alibrary);
1473 if (ft_status != 0)
1474 RelinquishMagickMemory(memory);
1475 else
1476 FT_Add_Default_Modules(*alibrary);
1477 FT_Set_Default_Properties(*alibrary);
1478 return(ft_status);
1479}
1480
1481static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
1482 const char *encoding,const PointInfo *offset,TypeMetric *metrics,
1483 ExceptionInfo *exception)
1484{
1485#if !defined(FT_OPEN_PATHNAME)
1486#define FT_OPEN_PATHNAME ft_open_pathname
1487#endif
1488
1489#define ThrowFreetypeErrorException(tag,ft_status,value) \
1490{ \
1491 const char *error_string = FreetypeErrorMessage(ft_status); \
1492 if (error_string != (const char *) NULL) \
1493 (void) ThrowMagickException(exception,GetMagickModule(),TypeError, \
1494 tag,"`%s (%s)'",value, error_string); \
1495 else \
1496 (void) ThrowMagickException(exception,GetMagickModule(),TypeError, \
1497 tag,"`%s'",value); \
1498}
1499
1500 typedef struct _GlyphInfo
1501 {
1502 FT_UInt
1503 id;
1504
1505 FT_Vector
1506 origin;
1507
1508 FT_Glyph
1509 image;
1510 } GlyphInfo;
1511
1512 char
1513 *p;
1514
1515 const char
1516 *value;
1517
1518 DrawInfo
1519 *annotate_info;
1520
1521 FT_BBox
1522 bounds;
1523
1524 FT_BitmapGlyph
1525 bitmap;
1526
1527 FT_Encoding
1528 encoding_type;
1529
1530 FT_Error
1531 ft_status;
1532
1533 FT_Face
1534 face;
1535
1536 FT_Int32
1537 flags;
1538
1539 FT_Library
1540 library;
1541
1542 FT_Long
1543 face_index;
1544
1545 FT_Matrix
1546 affine;
1547
1548 FT_Memory
1549 memory;
1550
1551 FT_Open_Args
1552 args;
1553
1554 FT_StreamRec
1555 *stream;
1556
1557 FT_UInt
1558 first_glyph_id,
1559 last_glyph_id,
1560 missing_glyph_id;
1561
1562 FT_Vector
1563 origin;
1564
1565 GlyphInfo
1566 glyph;
1567
1569 *grapheme;
1570
1571 MagickBooleanType
1572 status;
1573
1574 PointInfo
1575 point,
1576 resolution;
1577
1578 ssize_t
1579 i;
1580
1581 size_t
1582 length;
1583
1584 ssize_t
1585 code,
1586 last_character,
1587 y;
1588
1589 static FT_Outline_Funcs
1590 OutlineMethods =
1591 {
1592 (FT_Outline_MoveTo_Func) TraceMoveTo,
1593 (FT_Outline_LineTo_Func) TraceLineTo,
1594 (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1595 (FT_Outline_CubicTo_Func) TraceCubicBezier,
1596 0, 0
1597 };
1598
1599 struct stat
1600 attributes;
1601
1602 unsigned char
1603 *utf8;
1604
1605 /*
1606 Initialize Truetype library.
1607 */
1608 memory=FreetypeAcquireMemoryManager();
1609 if (memory == (FT_Memory) NULL)
1610 ThrowBinaryException(ResourceLimitError,"UnableToInitializeFreetypeLibrary",
1611 image->filename);
1612 ft_status=FreetypeInit(memory,&library);
1613 if (ft_status != 0)
1614 {
1615 ThrowFreetypeErrorException("UnableToInitializeFreetypeLibrary",ft_status,
1616 image->filename);
1617 return(MagickFalse);
1618 }
1619 /*
1620 Open font face.
1621 */
1622 face_index=(FT_Long) draw_info->face;
1623 (void) memset(&args,0,sizeof(args));
1624 if (draw_info->font == (char *) NULL)
1625 {
1626 const TypeInfo *type_info = GetTypeInfo("*",exception);
1627 if (type_info != (const TypeInfo *) NULL)
1628 args.pathname=ConstantString(type_info->glyphs);
1629 }
1630 else
1631 if (*draw_info->font != '@')
1632 args.pathname=ConstantString(draw_info->font);
1633 else
1634 {
1635 /*
1636 Extract face index, e.g. @msgothic[1].
1637 */
1638 ImageInfo *image_info = AcquireImageInfo();
1639 (void) CopyMagickString(image_info->filename,draw_info->font+1,
1640 MagickPathExtent);
1641 (void) SetImageInfo(image_info,0,exception);
1642 face_index=(FT_Long) image_info->scene;
1643 args.pathname=ConstantString(image_info->filename);
1644 image_info=DestroyImageInfo(image_info);
1645 }
1646 /*
1647 Configure streaming interface.
1648 */
1649 stream=(FT_StreamRec *) AcquireCriticalMemory(sizeof(*stream));
1650 (void) memset(stream,0,sizeof(*stream));
1651 if (stat(args.pathname,&attributes) == 0)
1652 stream->size=attributes.st_size >= 0 ? (unsigned long)
1653 attributes.st_size : 0;
1654 stream->descriptor.pointer=fopen_utf8(args.pathname,"rb");
1655 stream->read=(&FreetypeReadStream);
1656 stream->close=(&FreetypeCloseStream);
1657 args.flags=FT_OPEN_STREAM;
1658 args.stream=stream;
1659 face=(FT_Face) NULL;
1660 ft_status=FT_Open_Face(library,&args,face_index,&face);
1661 if (ft_status != 0)
1662 {
1663 FreetypeDone(memory,library,stream);
1664 ThrowFreetypeErrorException("UnableToReadFont",ft_status,args.pathname);
1665 args.pathname=DestroyString(args.pathname);
1666 return(MagickFalse);
1667 }
1668 args.pathname=DestroyString(args.pathname);
1669 if ((draw_info->metrics != (char *) NULL) &&
1670 (IsPathAccessible(draw_info->metrics) != MagickFalse))
1671 (void) FT_Attach_File(face,draw_info->metrics);
1672 encoding_type=FT_ENCODING_UNICODE;
1673 ft_status=FT_Select_Charmap(face,encoding_type);
1674 if ((ft_status != 0) && (face->num_charmaps != 0))
1675 ft_status=FT_Set_Charmap(face,face->charmaps[0]);
1676 if (encoding != (const char *) NULL)
1677 {
1678 if (LocaleCompare(encoding,"AdobeCustom") == 0)
1679 encoding_type=FT_ENCODING_ADOBE_CUSTOM;
1680 if (LocaleCompare(encoding,"AdobeExpert") == 0)
1681 encoding_type=FT_ENCODING_ADOBE_EXPERT;
1682 if (LocaleCompare(encoding,"AdobeStandard") == 0)
1683 encoding_type=FT_ENCODING_ADOBE_STANDARD;
1684 if (LocaleCompare(encoding,"AppleRoman") == 0)
1685 encoding_type=FT_ENCODING_APPLE_ROMAN;
1686 if (LocaleCompare(encoding,"BIG5") == 0)
1687 encoding_type=FT_ENCODING_BIG5;
1688#if defined(FT_ENCODING_PRC)
1689 if (LocaleCompare(encoding,"GB2312") == 0)
1690 encoding_type=FT_ENCODING_PRC;
1691#endif
1692#if defined(FT_ENCODING_JOHAB)
1693 if (LocaleCompare(encoding,"Johab") == 0)
1694 encoding_type=FT_ENCODING_JOHAB;
1695#endif
1696#if defined(FT_ENCODING_ADOBE_LATIN_1)
1697 if (LocaleCompare(encoding,"Latin-1") == 0)
1698 encoding_type=FT_ENCODING_ADOBE_LATIN_1;
1699#endif
1700#if defined(FT_ENCODING_ADOBE_LATIN_2)
1701 if (LocaleCompare(encoding,"Latin-2") == 0)
1702 encoding_type=FT_ENCODING_OLD_LATIN_2;
1703#endif
1704 if (LocaleCompare(encoding,"None") == 0)
1705 encoding_type=FT_ENCODING_NONE;
1706 if (LocaleCompare(encoding,"SJIScode") == 0)
1707 encoding_type=FT_ENCODING_SJIS;
1708 if (LocaleCompare(encoding,"Symbol") == 0)
1709 encoding_type=FT_ENCODING_MS_SYMBOL;
1710 if (LocaleCompare(encoding,"Unicode") == 0)
1711 encoding_type=FT_ENCODING_UNICODE;
1712 if (LocaleCompare(encoding,"Wansung") == 0)
1713 encoding_type=FT_ENCODING_WANSUNG;
1714 ft_status=FT_Select_Charmap(face,encoding_type);
1715 if (ft_status != 0)
1716 {
1717 (void) FT_Done_Face(face);
1718 FreetypeDone(memory,library,stream);
1719 ThrowFreetypeErrorException("UnrecognizedFontEncoding",ft_status,
1720 encoding);
1721 return(MagickFalse);
1722 }
1723 }
1724 /*
1725 Set text size.
1726 */
1727 resolution.x=DefaultResolution;
1728 resolution.y=DefaultResolution;
1729 if (draw_info->density != (char *) NULL)
1730 {
1732 geometry_info;
1733
1734 MagickStatusType
1735 geometry_flags;
1736
1737 geometry_flags=ParseGeometry(draw_info->density,&geometry_info);
1738 if ((geometry_flags & RhoValue) != 0)
1739 resolution.x=geometry_info.rho;
1740 resolution.y=resolution.x;
1741 if ((geometry_flags & SigmaValue) != 0)
1742 resolution.y=geometry_info.sigma;
1743 }
1744 ft_status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1745 (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1746 (FT_UInt) resolution.y);
1747 if (ft_status != 0)
1748 {
1749 (void) FT_Done_Face(face);
1750 FreetypeDone(memory,library,stream);
1751 ThrowFreetypeErrorException("UnableToReadFont",ft_status,
1752 draw_info->font);
1753 return(MagickFalse);
1754 }
1755 metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1756 metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1757 metrics->ascent=(double) face->size->metrics.ascender/64.0;
1758 metrics->descent=(double) face->size->metrics.descender/64.0;
1759 if (face->size->metrics.ascender == 0)
1760 {
1761 /*
1762 Sanitize buggy ascender and descender values.
1763 */
1764 metrics->ascent=face->size->metrics.y_ppem;
1765 if (face->size->metrics.descender == 0)
1766 metrics->descent=face->size->metrics.y_ppem/-3.5;
1767 }
1768 metrics->width=0;
1769 metrics->origin.x=0;
1770 metrics->origin.y=0;
1771 metrics->height=(double) face->size->metrics.height/64.0;
1772 metrics->max_advance=0.0;
1773 if (face->size->metrics.max_advance > MagickEpsilon)
1774 metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1775 metrics->bounds.x1=0.0;
1776 metrics->bounds.y1=metrics->descent;
1777 metrics->bounds.x2=metrics->ascent+metrics->descent;
1778 metrics->bounds.y2=metrics->ascent+metrics->descent;
1779 metrics->underline_position=face->underline_position*
1780 (metrics->pixels_per_em.x*PerceptibleReciprocal(face->units_per_EM));
1781 metrics->underline_thickness=face->underline_thickness*
1782 (metrics->pixels_per_em.x*PerceptibleReciprocal(face->units_per_EM));
1783 first_glyph_id=0;
1784 FT_Get_First_Char(face,&first_glyph_id);
1785 if ((draw_info->text == (char *) NULL) || (*draw_info->text == '\0') ||
1786 (first_glyph_id == 0))
1787 {
1788 (void) FT_Done_Face(face);
1789 FreetypeDone(memory,library,stream);
1790 return(MagickTrue);
1791 }
1792 /*
1793 Compute bounding box.
1794 */
1795 if (draw_info->debug != MagickFalse)
1796 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
1797 "font-encoding %s; text-encoding %s; pointsize %g",
1798 draw_info->font != (char *) NULL ? draw_info->font : "none",
1799 encoding != (char *) NULL ? encoding : "none",
1800 draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1801 draw_info->pointsize);
1802 flags=FT_LOAD_DEFAULT;
1803 if (draw_info->render == MagickFalse)
1804 flags=FT_LOAD_NO_BITMAP;
1805 if (draw_info->text_antialias == MagickFalse)
1806 flags|=FT_LOAD_TARGET_MONO;
1807 else
1808 {
1809#if defined(FT_LOAD_TARGET_LIGHT)
1810 flags|=FT_LOAD_TARGET_LIGHT;
1811#elif defined(FT_LOAD_TARGET_LCD)
1812 flags|=FT_LOAD_TARGET_LCD;
1813#endif
1814 }
1815 value=GetImageProperty(image,"type:hinting",exception);
1816 if ((value != (const char *) NULL) && (LocaleCompare(value,"off") == 0))
1817 flags|=FT_LOAD_NO_HINTING;
1818 glyph.id=0;
1819 glyph.image=(FT_Glyph) NULL;
1820 last_glyph_id=0;
1821 origin.x=0;
1822 origin.y=0;
1823 affine.xx=65536L;
1824 affine.yx=0L;
1825 affine.xy=0L;
1826 affine.yy=65536L;
1827 if (draw_info->render != MagickFalse)
1828 {
1829 affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1830 affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1831 affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1832 affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1833 }
1834 annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1835 if (annotate_info->dash_pattern != (double *) NULL)
1836 annotate_info->dash_pattern[0]=0.0;
1837 (void) CloneString(&annotate_info->primitive,"path '");
1838 status=MagickTrue;
1839 if (draw_info->render != MagickFalse)
1840 {
1841 if (image->storage_class != DirectClass)
1842 (void) SetImageStorageClass(image,DirectClass,exception);
1843 if ((image->alpha_trait & BlendPixelTrait) == 0)
1844 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1845 }
1846 for (p=draw_info->text; GetUTFCode(p) != 0; p+=(ptrdiff_t) GetUTFOctets(p))
1847 if (GetUTFCode(p) < 0)
1848 break;
1849 utf8=(unsigned char *) NULL;
1850 if (GetUTFCode(p) == 0)
1851 p=draw_info->text;
1852 else
1853 {
1854 utf8=ConvertLatin1ToUTF8((unsigned char *) draw_info->text);
1855 if (utf8 != (unsigned char *) NULL)
1856 p=(char *) utf8;
1857 }
1858 grapheme=(GraphemeInfo *) NULL;
1859#if defined(MAGICKCORE_RAQM_DELEGATE)
1860 length=ComplexRaqmTextLayout(image,draw_info,p,strlen(p),face,&grapheme,
1861 exception);
1862#else
1863 length=ComplexTextLayout(draw_info,p,strlen(p),face,flags,&grapheme);
1864#endif
1865 missing_glyph_id=FT_Get_Char_Index(face,' ');
1866 code=0;
1867 last_character=(ssize_t) length-1;
1868 for (i=0; i < (ssize_t) length; i++)
1869 {
1870 FT_Outline
1871 outline;
1872
1873 /*
1874 Render UTF-8 sequence.
1875 */
1876 glyph.id=(FT_UInt) grapheme[i].index;
1877 if (glyph.id == 0)
1878 glyph.id=missing_glyph_id;
1879 if ((glyph.id != 0) && (last_glyph_id != 0))
1880 origin.x+=(FT_Pos) (64.0*draw_info->kerning);
1881 glyph.origin=origin;
1882 glyph.origin.x+=(FT_Pos) grapheme[i].x_offset;
1883 glyph.origin.y+=(FT_Pos) grapheme[i].y_offset;
1884 if (glyph.image != (FT_Glyph) NULL)
1885 {
1886 FT_Done_Glyph(glyph.image);
1887 glyph.image=(FT_Glyph) NULL;
1888 }
1889 ft_status=FT_Load_Glyph(face,glyph.id,flags);
1890 if (ft_status != 0)
1891 continue;
1892 ft_status=FT_Get_Glyph(face->glyph,&glyph.image);
1893 if (ft_status != 0)
1894 continue;
1895 outline=((FT_OutlineGlyph) glyph.image)->outline;
1896 if ((glyph.image->format != FT_GLYPH_FORMAT_OUTLINE) &&
1897 (IsEmptyOutline(outline) == MagickFalse))
1898 continue;
1899 ft_status=FT_Outline_Get_BBox(&outline,&bounds);
1900 if (ft_status != 0)
1901 continue;
1902 if ((bounds.xMin < metrics->bounds.x1) && (bounds.xMin != 0))
1903 metrics->bounds.x1=(double) bounds.xMin;
1904 if ((bounds.yMin < metrics->bounds.y1) && (bounds.yMin != 0))
1905 metrics->bounds.y1=(double) bounds.yMin;
1906 if ((bounds.xMax > metrics->bounds.x2) && (bounds.xMax != 0))
1907 metrics->bounds.x2=(double) bounds.xMax;
1908 if ((bounds.yMax > metrics->bounds.y2) && (bounds.yMax != 0))
1909 metrics->bounds.y2=(double) bounds.yMax;
1910 if (((draw_info->stroke.alpha != (MagickRealType) TransparentAlpha) ||
1911 (draw_info->stroke_pattern != (Image *) NULL)) &&
1912 ((status != MagickFalse) && (draw_info->render != MagickFalse)))
1913 {
1914 /*
1915 Trace the glyph.
1916 */
1917 annotate_info->affine.tx=glyph.origin.x/64.0;
1918 annotate_info->affine.ty=(-glyph.origin.y/64.0);
1919 if (IsEmptyOutline(outline) == MagickFalse)
1920 ft_status=FT_Outline_Decompose(&outline,&OutlineMethods,
1921 annotate_info);
1922 }
1923 FT_Vector_Transform(&glyph.origin,&affine);
1924 (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1925 ft_status=FT_Glyph_To_Bitmap(&glyph.image,FT_RENDER_MODE_NORMAL,
1926 (FT_Vector *) NULL,MagickTrue);
1927 if (ft_status != 0)
1928 continue;
1929 bitmap=(FT_BitmapGlyph) glyph.image;
1930 point.x=offset->x+bitmap->left;
1931 if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1932 point.x+=(origin.x/64.0);
1933 point.y=offset->y-bitmap->top;
1934 if (draw_info->render != MagickFalse)
1935 {
1936 CacheView
1937 *image_view;
1938
1939 MagickBooleanType
1940 transparent_fill;
1941
1942 unsigned char
1943 *r;
1944
1945 /*
1946 Rasterize the glyph.
1947 */
1948 transparent_fill=((draw_info->fill.alpha == (MagickRealType) TransparentAlpha) &&
1949 (draw_info->fill_pattern == (Image *) NULL) &&
1950 (draw_info->stroke.alpha == (MagickRealType) TransparentAlpha) &&
1951 (draw_info->stroke_pattern == (Image *) NULL)) ? MagickTrue :
1952 MagickFalse;
1953 r=bitmap->bitmap.buffer;
1954 image_view=AcquireAuthenticCacheView(image,exception);
1955#if defined(MAGICKCORE_OPENMP_SUPPORT)
1956 #pragma omp parallel for schedule(static) shared(status) \
1957 magick_number_threads(image,image,bitmap->bitmap.rows,4)
1958#endif
1959 for (y=0; y < (ssize_t) bitmap->bitmap.rows; y++)
1960 {
1961 double
1962 fill_opacity;
1963
1964 MagickBooleanType
1965 active,
1966 sync;
1967
1968 PixelInfo
1969 fill_color;
1970
1971 Quantum
1972 *magick_restrict q;
1973
1974 ssize_t
1975 x;
1976
1977 ssize_t
1978 n,
1979 x_offset,
1980 y_offset;
1981
1982 if (status == MagickFalse)
1983 continue;
1984 x_offset=CastDoubleToLong(ceil(point.x-0.5));
1985 y_offset=CastDoubleToLong(ceil(point.y+y-0.5));
1986 if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
1987 continue;
1988 q=(Quantum *) NULL;
1989 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
1990 active=MagickFalse;
1991 else
1992 {
1993 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
1994 bitmap->bitmap.width,1,exception);
1995 active=q != (Quantum *) NULL ? MagickTrue : MagickFalse;
1996 }
1997 n=y*bitmap->bitmap.pitch;
1998 for (x=0; x < (ssize_t) bitmap->bitmap.width; x++, n++)
1999 {
2000 x_offset=CastDoubleToLong(ceil(point.x+x-0.5));
2001 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
2002 {
2003 if (q != (Quantum *) NULL)
2004 q+=(ptrdiff_t) GetPixelChannels(image);
2005 continue;
2006 }
2007 fill_opacity=1.0;
2008 if (bitmap->bitmap.buffer != (unsigned char *) NULL)
2009 {
2010 if (bitmap->bitmap.pixel_mode == ft_pixel_mode_grays)
2011 fill_opacity=(double) (r[n])/(bitmap->bitmap.num_grays-1);
2012 else
2013 if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
2014 fill_opacity=((r[(x >> 3)+y*bitmap->bitmap.pitch] &
2015 (1 << (~x & 0x07)))) == 0 ? 0.0 : 1.0;
2016 }
2017 if (draw_info->text_antialias == MagickFalse)
2018 fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
2019 if (active == MagickFalse)
2020 q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
2021 exception);
2022 if (q == (Quantum *) NULL)
2023 continue;
2024 if (transparent_fill == MagickFalse)
2025 {
2026 GetPixelInfo(image,&fill_color);
2027 GetFillColor(draw_info,x_offset,y_offset,&fill_color,exception);
2028 fill_opacity=fill_opacity*fill_color.alpha;
2029 CompositePixelOver(image,&fill_color,fill_opacity,q,
2030 GetPixelAlpha(image,q),q);
2031 }
2032 else
2033 {
2034 double
2035 Sa,
2036 Da;
2037
2038 Da=1.0-(QuantumScale*(double) GetPixelAlpha(image,q));
2039 Sa=fill_opacity;
2040 fill_opacity=(1.0-RoundToUnity(Sa+Da-Sa*Da))*(double)
2041 QuantumRange;
2042 SetPixelAlpha(image,fill_opacity,q);
2043 }
2044 if (active == MagickFalse)
2045 {
2046 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2047 if (sync == MagickFalse)
2048 status=MagickFalse;
2049 }
2050 q+=(ptrdiff_t) GetPixelChannels(image);
2051 }
2052 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2053 if (sync == MagickFalse)
2054 status=MagickFalse;
2055 }
2056 image_view=DestroyCacheView(image_view);
2057 if (((draw_info->stroke.alpha != (MagickRealType) TransparentAlpha) ||
2058 (draw_info->stroke_pattern != (Image *) NULL)) &&
2059 (status != MagickFalse))
2060 {
2061 /*
2062 Draw text stroke.
2063 */
2064 annotate_info->linejoin=RoundJoin;
2065 annotate_info->affine.tx=offset->x;
2066 annotate_info->affine.ty=offset->y;
2067 (void) ConcatenateString(&annotate_info->primitive,"'");
2068 if (strlen(annotate_info->primitive) > 7)
2069 (void) DrawImage(image,annotate_info,exception);
2070 (void) CloneString(&annotate_info->primitive,"path '");
2071 }
2072 }
2073 if ((fabs(draw_info->interword_spacing) >= MagickEpsilon) &&
2074 (IsUTFSpace(GetUTFCode(p+grapheme[i].cluster)) != MagickFalse) &&
2075 (IsUTFSpace(code) == MagickFalse))
2076 origin.x+=(FT_Pos) (64.0*draw_info->interword_spacing);
2077 else
2078 if (i == last_character)
2079 origin.x+=MagickMax((FT_Pos) grapheme[i].x_advance,bounds.xMax);
2080 else
2081 origin.x+=(FT_Pos) grapheme[i].x_advance;
2082 origin.y+=(FT_Pos) grapheme[i].y_advance;
2083 metrics->origin.x=(double) origin.x;
2084 metrics->origin.y=(double) origin.y;
2085 if (metrics->origin.x > metrics->width)
2086 metrics->width=metrics->origin.x;
2087 last_glyph_id=glyph.id;
2088 code=GetUTFCode(p+grapheme[i].cluster);
2089 }
2090 if (grapheme != (GraphemeInfo *) NULL)
2091 grapheme=(GraphemeInfo *) RelinquishMagickMemory(grapheme);
2092 if (utf8 != (unsigned char *) NULL)
2093 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
2094 if (glyph.image != (FT_Glyph) NULL)
2095 {
2096 FT_Done_Glyph(glyph.image);
2097 glyph.image=(FT_Glyph) NULL;
2098 }
2099 /*
2100 Determine font metrics.
2101 */
2102 metrics->bounds.x1/=64.0;
2103 metrics->bounds.y1/=64.0;
2104 metrics->bounds.x2/=64.0;
2105 metrics->bounds.y2/=64.0;
2106 metrics->origin.x/=64.0;
2107 metrics->origin.y/=64.0;
2108 metrics->width=ceil(metrics->width/64.0);
2109 /*
2110 Relinquish resources.
2111 */
2112 annotate_info=DestroyDrawInfo(annotate_info);
2113 (void) FT_Done_Face(face);
2114 FreetypeDone(memory,library,stream);
2115 return(status);
2116}
2117#else
2118static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
2119 const char *magick_unused(encoding),const PointInfo *offset,
2120 TypeMetric *metrics,ExceptionInfo *exception)
2121{
2122 magick_unreferenced(encoding);
2123 (void) ThrowMagickException(exception,GetMagickModule(),
2124 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (Freetype)",
2125 draw_info->font != (char *) NULL ? draw_info->font : "none");
2126 return(RenderPostscript(image,draw_info,offset,metrics,exception));
2127}
2128#endif
2129␌
2130/*
2131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2132% %
2133% %
2134% %
2135+ R e n d e r P o s t s c r i p t %
2136% %
2137% %
2138% %
2139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2140%
2141% RenderPostscript() renders text on the image with a Postscript font. It
2142% also returns the bounding box of the text relative to the image.
2143%
2144% The format of the RenderPostscript method is:
2145%
2146% MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
2147% const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2148%
2149% A description of each parameter follows:
2150%
2151% o image: the image.
2152%
2153% o draw_info: the draw info.
2154%
2155% o offset: (x,y) location of text relative to image.
2156%
2157% o metrics: bounding box of text.
2158%
2159% o exception: return any errors or warnings in this structure.
2160%
2161*/
2162
2163static MagickBooleanType RenderPostscript(Image *image,
2164 const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics,
2165 ExceptionInfo *exception)
2166{
2167 char
2168 filename[MagickPathExtent],
2169 geometry[MagickPathExtent],
2170 *text;
2171
2172 FILE
2173 *file;
2174
2175 Image
2176 *annotate_image;
2177
2178 ImageInfo
2179 *annotate_info;
2180
2181 int
2182 unique_file;
2183
2184 MagickBooleanType
2185 identity,
2186 status;
2187
2188 PointInfo
2189 extent,
2190 point,
2191 resolution;
2192
2193 ssize_t
2194 i;
2195
2196 size_t
2197 length;
2198
2199 ssize_t
2200 y;
2201
2202 /*
2203 Render label with a Postscript font.
2204 */
2205 if (draw_info->debug != MagickFalse)
2206 (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
2207 "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
2208 draw_info->font : "none",draw_info->pointsize);
2209 file=(FILE *) NULL;
2210 unique_file=AcquireUniqueFileResource(filename);
2211 if (unique_file != -1)
2212 file=fdopen(unique_file,"wb");
2213 if ((unique_file == -1) || (file == (FILE *) NULL))
2214 {
2215 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",filename);
2216 return(MagickFalse);
2217 }
2218 (void) FormatLocaleFile(file,"%%!PS-Adobe-3.0\n");
2219 (void) FormatLocaleFile(file,"/ReencodeType\n");
2220 (void) FormatLocaleFile(file,"{\n");
2221 (void) FormatLocaleFile(file," findfont dup length\n");
2222 (void) FormatLocaleFile(file,
2223 " dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
2224 (void) FormatLocaleFile(file,
2225 " /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
2226 (void) FormatLocaleFile(file,"} bind def\n");
2227 /*
2228 Sample to compute bounding box.
2229 */
2230 identity=(fabs(draw_info->affine.sx-draw_info->affine.sy) < MagickEpsilon) &&
2231 (fabs(draw_info->affine.rx) < MagickEpsilon) &&
2232 (fabs(draw_info->affine.ry) < MagickEpsilon) ? MagickTrue : MagickFalse;
2233 extent.x=0.0;
2234 extent.y=0.0;
2235 length=strlen(draw_info->text);
2236 for (i=0; i <= (ssize_t) (length+2); i++)
2237 {
2238 point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
2239 draw_info->affine.ry*2.0*draw_info->pointsize);
2240 point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
2241 draw_info->affine.sy*2.0*draw_info->pointsize);
2242 if (point.x > extent.x)
2243 extent.x=point.x;
2244 if (point.y > extent.y)
2245 extent.y=point.y;
2246 }
2247 (void) FormatLocaleFile(file,"%g %g moveto\n",identity != MagickFalse ? 0.0 :
2248 extent.x/2.0,extent.y/2.0);
2249 (void) FormatLocaleFile(file,"%g %g scale\n",draw_info->pointsize,
2250 draw_info->pointsize);
2251 if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
2252 (strchr(draw_info->font,'/') != (char *) NULL))
2253 (void) FormatLocaleFile(file,
2254 "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
2255 else
2256 (void) FormatLocaleFile(file,
2257 "/%s-ISO dup /%s ReencodeType findfont setfont\n",draw_info->font,
2258 draw_info->font);
2259 (void) FormatLocaleFile(file,"[%g %g %g %g 0 0] concat\n",
2260 draw_info->affine.sx,-draw_info->affine.rx,-draw_info->affine.ry,
2261 draw_info->affine.sy);
2262 text=EscapeParenthesis(draw_info->text);
2263 if (identity == MagickFalse)
2264 (void) FormatLocaleFile(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",
2265 text);
2266 (void) FormatLocaleFile(file,"(%s) show\n",text);
2267 text=DestroyString(text);
2268 (void) FormatLocaleFile(file,"showpage\n");
2269 (void) fclose(file);
2270 (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0!",
2271 floor(extent.x+0.5),floor(extent.y+0.5));
2272 annotate_info=AcquireImageInfo();
2273 (void) FormatLocaleString(annotate_info->filename,MagickPathExtent,"ps:%s",
2274 filename);
2275 (void) CloneString(&annotate_info->page,geometry);
2276 if (draw_info->density != (char *) NULL)
2277 (void) CloneString(&annotate_info->density,draw_info->density);
2278 annotate_info->antialias=draw_info->text_antialias;
2279 annotate_image=ReadImage(annotate_info,exception);
2280 CatchException(exception);
2281 annotate_info=DestroyImageInfo(annotate_info);
2282 (void) RelinquishUniqueFileResource(filename);
2283 if (annotate_image == (Image *) NULL)
2284 return(MagickFalse);
2285 (void) NegateImage(annotate_image,MagickFalse,exception);
2286 resolution.x=DefaultResolution;
2287 resolution.y=DefaultResolution;
2288 if (draw_info->density != (char *) NULL)
2289 {
2291 geometry_info;
2292
2293 MagickStatusType
2294 flags;
2295
2296 flags=ParseGeometry(draw_info->density,&geometry_info);
2297 if ((flags & RhoValue) != 0)
2298 resolution.x=geometry_info.rho;
2299 resolution.y=resolution.x;
2300 if ((flags & SigmaValue) != 0)
2301 resolution.y=geometry_info.sigma;
2302 }
2303 if (identity == MagickFalse)
2304 (void) TransformImage(&annotate_image,"0x0",(char *) NULL,exception);
2305 else
2306 {
2308 crop_info;
2309
2310 crop_info=GetImageBoundingBox(annotate_image,exception);
2311 crop_info.height=(size_t) ((resolution.y/DefaultResolution)*
2312 ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
2313 crop_info.y=CastDoubleToLong(ceil((resolution.y/DefaultResolution)*
2314 extent.y/8.0-0.5));
2315 (void) FormatLocaleString(geometry,MagickPathExtent,
2316 "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
2317 crop_info.height,(double) crop_info.x,(double) crop_info.y);
2318 (void) TransformImage(&annotate_image,geometry,(char *) NULL,exception);
2319 }
2320 metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
2321 ExpandAffine(&draw_info->affine)*draw_info->pointsize;
2322 metrics->pixels_per_em.y=metrics->pixels_per_em.x;
2323 metrics->ascent=metrics->pixels_per_em.y;
2324 metrics->descent=metrics->pixels_per_em.y/-5.0;
2325 metrics->width=(double) annotate_image->columns/
2326 ExpandAffine(&draw_info->affine);
2327 metrics->height=floor(metrics->ascent-metrics->descent+0.5);
2328 metrics->max_advance=metrics->pixels_per_em.x;
2329 metrics->bounds.x1=0.0;
2330 metrics->bounds.y1=metrics->descent;
2331 metrics->bounds.x2=metrics->ascent+metrics->descent;
2332 metrics->bounds.y2=metrics->ascent+metrics->descent;
2333 metrics->underline_position=(-2.0);
2334 metrics->underline_thickness=1.0;
2335 if (draw_info->render == MagickFalse)
2336 {
2337 annotate_image=DestroyImage(annotate_image);
2338 return(MagickTrue);
2339 }
2340 if (draw_info->fill.alpha != (MagickRealType) TransparentAlpha)
2341 {
2342 CacheView
2343 *annotate_view;
2344
2345 MagickBooleanType
2346 sync;
2347
2348 PixelInfo
2349 fill_color;
2350
2351 /*
2352 Render fill color.
2353 */
2354 if ((image->alpha_trait & BlendPixelTrait) == 0)
2355 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2356 if (annotate_image->alpha_trait == UndefinedPixelTrait)
2357 (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel,
2358 exception);
2359 fill_color=draw_info->fill;
2360 status=MagickTrue;
2361 annotate_view=AcquireAuthenticCacheView(annotate_image,exception);
2362#if defined(MAGICKCORE_OPENMP_SUPPORT)
2363 #pragma omp parallel for schedule(static) shared(status) \
2364 magick_number_threads(annotate_image,annotate_image,annotate_image->rows,4)
2365#endif
2366 for (y=0; y < (ssize_t) annotate_image->rows; y++)
2367 {
2368 ssize_t
2369 x;
2370
2371 Quantum
2372 *magick_restrict q;
2373
2374 if (status == MagickFalse)
2375 continue;
2376 q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
2377 1,exception);
2378 if (q == (Quantum *) NULL)
2379 {
2380 status=MagickFalse;
2381 continue;
2382 }
2383 for (x=0; x < (ssize_t) annotate_image->columns; x++)
2384 {
2385 GetFillColor(draw_info,x,y,&fill_color,exception);
2386 SetPixelAlpha(annotate_image,ClampToQuantum((((double) QuantumScale*
2387 GetPixelIntensity(annotate_image,q)*fill_color.alpha))),q);
2388 SetPixelRed(annotate_image,fill_color.red,q);
2389 SetPixelGreen(annotate_image,fill_color.green,q);
2390 SetPixelBlue(annotate_image,fill_color.blue,q);
2391 q+=(ptrdiff_t) GetPixelChannels(annotate_image);
2392 }
2393 sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
2394 if (sync == MagickFalse)
2395 status=MagickFalse;
2396 }
2397 annotate_view=DestroyCacheView(annotate_view);
2398 (void) CompositeImage(image,annotate_image,OverCompositeOp,MagickTrue,
2399 (ssize_t) ceil(offset->x-0.5),(ssize_t) ceil(offset->y-(metrics->ascent+
2400 metrics->descent)-0.5),exception);
2401 }
2402 annotate_image=DestroyImage(annotate_image);
2403 return(MagickTrue);
2404}
2405␌
2406/*
2407%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2408% %
2409% %
2410% %
2411+ R e n d e r X 1 1 %
2412% %
2413% %
2414% %
2415%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2416%
2417% RenderX11() renders text on the image with an X11 font. It also returns the
2418% bounding box of the text relative to the image.
2419%
2420% The format of the RenderX11 method is:
2421%
2422% MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
2423% const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2424%
2425% A description of each parameter follows:
2426%
2427% o image: the image.
2428%
2429% o draw_info: the draw info.
2430%
2431% o offset: (x,y) location of text relative to image.
2432%
2433% o metrics: bounding box of text.
2434%
2435% o exception: return any errors or warnings in this structure.
2436%
2437*/
2438static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
2439 const PointInfo *offset,TypeMetric *metrics,ExceptionInfo *exception)
2440{
2441 MagickBooleanType
2442 status;
2443
2444 if (annotate_semaphore == (SemaphoreInfo *) NULL)
2445 ActivateSemaphoreInfo(&annotate_semaphore);
2446 LockSemaphoreInfo(annotate_semaphore);
2447 status=XRenderImage(image,draw_info,offset,metrics,exception);
2448 UnlockSemaphoreInfo(annotate_semaphore);
2449 return(status);
2450}