Coverage Report

Created: 2025-11-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/annotate.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003 - 2018 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%           AAA   N   N  N   N   OOO   TTTTT   AAA   TTTTT  EEEEE             %
15
%          A   A  NN  N  NN  N  O   O    T    A   A    T    E                 %
16
%          AAAAA  N N N  N N N  O   O    T    AAAAA    T    EEE               %
17
%          A   A  N  NN  N  NN  O   O    T    A   A    T    E                 %
18
%          A   A  N   N  N   N   OOO     T    A   A    T    EEEEE             %
19
%                                                                             %
20
%                                                                             %
21
%                  GraphicsMagick Image Annotation Methods                    %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1992                                   %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
% Digital Applications (www.digapp.com) contributed the stroked text algorithm.
33
% It was written by Leonard Rosenthol.
34
%
35
%
36
*/
37

38
/*
39
  Include declarations.
40
*/
41
#include "magick/studio.h"
42
#include "magick/alpha_composite.h"
43
#include "magick/analyze.h"
44
#include "magick/color.h"
45
#include "magick/color_lookup.h"
46
#include "magick/composite.h"
47
#include "magick/constitute.h"
48
#include "magick/gem.h"
49
#include "magick/log.h"
50
#include "magick/pixel_cache.h"
51
#include "magick/render.h"
52
#include "magick/tempfile.h"
53
#include "magick/transform.h"
54
#include "magick/utility.h"
55
#include "magick/xwindow.h"
56
#if defined(HasTTF)
57
58
#  if defined(__MINGW32__)
59
#    undef interface  /* Remove interface define */
60
#  endif
61
62
#  if defined(HAVE_FT2BUILD_H)
63
     /*
64
       Modern FreeType2 installs require that <ft2build.h> be included
65
       before including other FreeType2 headers.  Including
66
       <ft2build.h> establishes definitions used by other FreeType2
67
       headers.
68
     */
69
#    include <ft2build.h>
70
#    include FT_FREETYPE_H
71
#    include FT_GLYPH_H
72
#    include FT_OUTLINE_H
73
#    include FT_BBOX_H
74
#  else
75
     /*
76
       Very old way to include FreeType2
77
     */
78
#    include <freetype/freetype.h>
79
#    include <freetype/ftglyph.h>
80
#    include <freetype/ftoutln.h>
81
#    include <freetype/ftbbox.h>
82
#  endif /* defined(HAVE_FT2BUILD_H) */
83
84
#endif /* defined(HasTTF) */
85

86
/*
87
  Forward declarations.
88
*/
89
typedef magick_int32_t magick_code_point_t;
90
91
static unsigned int
92
  RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
93
  RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
94
  RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
95
    TypeMetric *),
96
  RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *);
97

98
/*
99
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100
%                                                                             %
101
%                                                                             %
102
%                                                                             %
103
%   A n n o t a t e I m a g e                                                 %
104
%                                                                             %
105
%                                                                             %
106
%                                                                             %
107
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108
%
109
%  AnnotateImage() annotates an image with DrawInfo 'text' based on other
110
%  parameters from DrawInfo such as 'affine', 'align', 'decorate', and
111
%  'gravity'.
112
%
113
%  Originally this function additionally transformed 'text' using
114
%  TranslateText() but it no longer does so as of GraphicsMagick 1.3.32.
115
%
116
%  The format of the AnnotateImage method is:
117
%
118
%      MagickPassFail AnnotateImage(Image *image,DrawInfo *draw_info)
119
%
120
%  A description of each parameter follows:
121
%
122
%    o status: Method AnnotateImage returns MagickPass if the image is annotated
123
%      otherwise MagickFail.
124
%
125
%    o image: The image.
126
%
127
%    o draw_info: The draw info.
128
%
129
%
130
*/
131
MagickExport MagickPassFail AnnotateImage(Image *image,const DrawInfo *draw_info)
132
24.8k
{
133
24.8k
  char
134
24.8k
    primitive[MaxTextExtent],
135
24.8k
    *p,
136
24.8k
    *text,
137
24.8k
    **textlist;
138
139
24.8k
  DrawInfo
140
24.8k
    *annotate,
141
24.8k
    *clone_info;
142
143
24.8k
  PointInfo
144
24.8k
    offset;
145
146
24.8k
  RectangleInfo
147
24.8k
    geometry;
148
149
24.8k
  register size_t
150
24.8k
    i;
151
152
24.8k
  TypeMetric
153
24.8k
    metrics;
154
155
24.8k
  unsigned int
156
24.8k
    matte;
157
158
24.8k
  MagickPassFail
159
24.8k
    status=MagickPass;
160
161
24.8k
  unsigned long
162
24.8k
    height,
163
24.8k
    number_lines;
164
165
24.8k
  MagickBool
166
24.8k
    metrics_initialized = MagickFalse;
167
168
24.8k
  assert(image != (Image *) NULL);
169
24.8k
  assert(image->signature == MagickSignature);
170
24.8k
  assert(draw_info != (DrawInfo *) NULL);
171
24.8k
  assert(draw_info->signature == MagickSignature);
172
24.8k
  if (draw_info->text == (char *) NULL)
173
0
    return(MagickFail);
174
24.8k
  if (*draw_info->text == '\0')
175
19.8k
    return(MagickPass);
176
4.98k
  annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
177
4.98k
  text=annotate->text;
178
4.98k
  annotate->text=(char *) NULL;
179
4.98k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,annotate);
180
  /*
181
    Split text into list based on new-lines
182
  */
183
4.98k
  number_lines=1;
184
8.09M
  for (p=text; *p != '\0'; p++)
185
8.09M
    if (*p == '\n')
186
134k
      number_lines++;
187
4.98k
  textlist=MagickAllocateArray(char **,((size_t) number_lines+1),sizeof(char *));
188
4.98k
  if (textlist == (char **) NULL)
189
0
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
190
4.98k
                      UnableToConvertText);
191
4.98k
  p=text;
192
144k
  for (i=0; i < number_lines; i++)
193
139k
    {
194
139k
      char *q;
195
139k
      textlist[i]=p;
196
7.06M
      for (q=(char *) p; *q != '\0'; q++)
197
7.06M
          if ((*q == '\r') || (*q == '\n'))
198
136k
            break;
199
139k
      if (*q == '\r')
200
1.99k
        {
201
1.99k
          *q='\0';
202
1.99k
          q++;
203
1.99k
        }
204
139k
      *q='\0';
205
139k
      p=q+1;
206
139k
    }
207
4.98k
  textlist[i]=(char *) NULL;
208
209
4.98k
  SetGeometry(image,&geometry);
210
4.98k
  if (draw_info->geometry != (char *) NULL)
211
4.98k
    (void) GetGeometry(draw_info->geometry,&geometry.x,&geometry.y,
212
4.98k
      &geometry.width,&geometry.height);
213
4.98k
  matte=image->matte;
214
4.98k
  status=MagickPass;
215
18.9k
  for (i=0; textlist[i] != (char *) NULL; i++)
216
15.1k
  {
217
15.1k
    if (*textlist[i] == '\0')
218
13.9k
      continue;
219
    /*
220
      Position text relative to image.
221
    */
222
1.21k
    (void) CloneString(&annotate->text,textlist[i]);
223
1.21k
    if ((!metrics_initialized) || (annotate->gravity != NorthWestGravity))
224
1.21k
      {
225
1.21k
        metrics_initialized=MagickTrue;
226
1.21k
        (void) GetTypeMetrics(image,annotate,&metrics);
227
1.21k
      }
228
1.21k
    height=(unsigned long) (metrics.ascent-metrics.descent);
229
1.21k
    switch (annotate->gravity)
230
1.21k
    {
231
0
      case ForgetGravity:
232
601
      case NorthWestGravity:
233
601
      default:
234
601
      {
235
601
        offset.x=(double)geometry.x+(double)i*draw_info->affine.ry*height;
236
601
        offset.y=(double)geometry.y+(double)i*draw_info->affine.sy*height;
237
601
        break;
238
601
      }
239
612
      case NorthGravity:
240
612
      {
241
612
        offset.x=(double)geometry.x+(double)geometry.width/2+(double)i*draw_info->affine.ry*height-
242
612
          (double)draw_info->affine.sx*metrics.width/2.0;
243
612
        offset.y=(double)geometry.y+(double)i*draw_info->affine.sy*height-draw_info->affine.rx*
244
612
          metrics.width/2.0;
245
612
        break;
246
601
      }
247
0
      case NorthEastGravity:
248
0
      {
249
0
        offset.x=(geometry.width == 0 ? 1.0 : -1.0)*(double)geometry.x+(double)geometry.width+i*
250
0
          draw_info->affine.ry*height-(double)draw_info->affine.sx*metrics.width;
251
0
        offset.y=(double)geometry.y+(double)i*draw_info->affine.sy*height-(double)draw_info->affine.rx*
252
0
          metrics.width;
253
0
        break;
254
601
      }
255
1
      case WestGravity:
256
1
      {
257
1
        offset.x=(double)geometry.x+(double)i*draw_info->affine.ry*height+(double)draw_info->affine.ry*
258
1
          (metrics.ascent+metrics.descent-(double)(number_lines-1)*(double) height)/2.0;
259
1
        offset.y=(double) geometry.y+(double)geometry.height/2.0+(double)i*draw_info->affine.sy*height+
260
1
          (double)draw_info->affine.sy*((double)metrics.ascent+metrics.descent-(double)(number_lines-1)*
261
1
          height)/2.0;
262
1
        break;
263
601
      }
264
0
      case StaticGravity:
265
1
      case CenterGravity:
266
1
      {
267
1
        offset.x=(double)geometry.x+(double)geometry.width/2.0+(double)i*draw_info->affine.ry*height-
268
1
          (double)draw_info->affine.sx*metrics.width/2.0+draw_info->affine.ry*
269
1
          ((double)metrics.ascent+metrics.descent-(double)(number_lines-1)*height)/2.0;
270
1
        offset.y=(double)geometry.y+(double)geometry.height/2.0+(double)i*draw_info->affine.sy*height-
271
1
          (double)draw_info->affine.rx*metrics.width/2.0+(double)draw_info->affine.sy*
272
1
          ((double)metrics.ascent+metrics.descent-(double)(number_lines-1)*height)/2.0;
273
1
        break;
274
0
      }
275
1
      case EastGravity:
276
1
      {
277
1
        offset.x=(geometry.width == 0 ? 1.0 : -1.0)*(double)geometry.x+(double)geometry.width+(double)i*
278
1
          (double)draw_info->affine.ry*height-(double)draw_info->affine.sx*metrics.width+
279
1
          (double)draw_info->affine.ry*((double)metrics.ascent+(double)metrics.descent-(number_lines-1)*
280
1
          (double)height)/2.0;
281
1
        offset.y=(double)geometry.y+(double)geometry.height/2.0+(double)i*draw_info->affine.sy*height-
282
1
            (double)draw_info->affine.rx*metrics.width+(double)draw_info->affine.sy*
283
1
          ((double)metrics.ascent+metrics.descent-(number_lines-1)*(double)height)/2.0;
284
1
        break;
285
0
      }
286
0
      case SouthWestGravity:
287
0
      {
288
0
        offset.x= (double)geometry.x+ (double)i*draw_info->affine.ry*height-draw_info->affine.ry*
289
0
          (number_lines-1)*height;
290
0
        offset.y= (double)(geometry.height == 0 ? 1 : -1)*geometry.y+geometry.height+i*
291
0
          draw_info->affine.sy*height-draw_info->affine.sy*(number_lines-1)*
292
0
          height;
293
0
        break;
294
0
      }
295
1
      case SouthGravity:
296
1
      {
297
1
        offset.x= (double)geometry.x+(double)geometry.width/2.0+(double)i*draw_info->affine.ry*
298
1
          height- (double)draw_info->affine.sx*metrics.width/2.0-
299
1
            (double)draw_info->affine.ry*(number_lines-1)*height;
300
1
        offset.y=(geometry.height == 0 ? 1.0 : -1.0)*geometry.y+geometry.height+(double)i*
301
1
          draw_info->affine.sy*height-(double)draw_info->affine.rx*
302
1
          metrics.width/2.0-(double)draw_info->affine.sy*(number_lines-1)*height;
303
1
        break;
304
0
      }
305
0
      case SouthEastGravity:
306
0
      {
307
0
        offset.x=(geometry.width == 0 ? 1.0 : -1.0)*geometry.x+geometry.width+(double)i*
308
0
          draw_info->affine.ry*height-(double)draw_info->affine.sx*metrics.width-
309
0
            (double)draw_info->affine.ry*(number_lines-1)*height;
310
0
        offset.y=(geometry.height == 0 ? 1.0 : -1.0)*geometry.y+(double)geometry.height+i*
311
0
          draw_info->affine.sy*height-(double)draw_info->affine.rx*metrics.width-
312
0
            (double)draw_info->affine.sy*(number_lines-1)*height;
313
0
        break;
314
0
      }
315
1.21k
    }
316
1.21k
    switch (annotate->align)
317
1.21k
    {
318
0
      case LeftAlign:
319
0
      {
320
0
        offset.x=geometry.x+i*draw_info->affine.ry*height;
321
0
        offset.y=geometry.y+i*draw_info->affine.sy*height;
322
0
        break;
323
0
      }
324
1
      case CenterAlign:
325
1
      {
326
1
        offset.x=geometry.x+i*draw_info->affine.ry*height-draw_info->affine.sx*
327
1
          metrics.width/2;
328
1
        offset.y=geometry.y+i*draw_info->affine.sy*height-draw_info->affine.rx*
329
1
          metrics.width/2;
330
1
        break;
331
0
      }
332
0
      case RightAlign:
333
0
      {
334
0
        offset.x=geometry.x+i*draw_info->affine.ry*height-draw_info->affine.sx*
335
0
          metrics.width;
336
0
        offset.y=geometry.y+i*draw_info->affine.sy*height-draw_info->affine.rx*
337
0
          metrics.width;
338
0
        break;
339
0
      }
340
1.21k
      default:
341
1.21k
        break;
342
1.21k
    }
343
1.21k
    if (draw_info->undercolor.opacity != TransparentOpacity)
344
1
      {
345
        /*
346
          Text box.
347
        */
348
1
        clone_info->fill=draw_info->undercolor;
349
1
        clone_info->affine.tx=offset.x-draw_info->affine.ry*(metrics.ascent-
350
1
          metrics.max_advance/4);
351
1
        clone_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
352
1
        FormatString(primitive,"rectangle 0,0 %g,%ld",metrics.width+
353
1
          metrics.max_advance/2.0,height);
354
1
        (void) CloneString(&clone_info->primitive,primitive);
355
1
        (void) DrawImage(image,clone_info);
356
1
      }
357
1.21k
    clone_info->affine.tx=offset.x;
358
1.21k
    clone_info->affine.ty=offset.y;
359
1.21k
    FormatString(primitive,"stroke-width %g line 0,0 %g,0",
360
1.21k
      metrics.underline_thickness,metrics.width);
361
1.21k
    if (annotate->decorate == OverlineDecoration)
362
1
      {
363
1
        clone_info->affine.ty-=(draw_info->affine.sy*
364
1
          (metrics.ascent+metrics.descent)-metrics.underline_position);
365
1
        (void) CloneString(&clone_info->primitive,primitive);
366
1
        (void) DrawImage(image,clone_info);
367
1
      }
368
1.21k
    else
369
1.21k
      if (annotate->decorate == UnderlineDecoration)
370
0
        {
371
0
          clone_info->affine.ty-=metrics.underline_position;
372
0
          (void) CloneString(&clone_info->primitive,primitive);
373
0
          (void) DrawImage(image,clone_info);
374
0
        }
375
    /*
376
      Annotate image with text.
377
    */
378
1.21k
    status=RenderType(image,annotate,&offset,&metrics);
379
1.21k
    if (status == MagickFail)
380
1.21k
      break;
381
0
    if (annotate->decorate == LineThroughDecoration)
382
0
      {
383
0
        clone_info->affine.ty-=(draw_info->affine.sy*height+
384
0
          metrics.underline_position)/2.0;
385
0
        (void) CloneString(&clone_info->primitive,primitive);
386
0
        (void) DrawImage(image,clone_info);
387
0
      }
388
0
  }
389
4.98k
  image->matte=matte;
390
  /*
391
    Free resources.
392
  */
393
4.98k
  DestroyDrawInfo(clone_info);
394
4.98k
  DestroyDrawInfo(annotate);
395
4.98k
  MagickFreeMemory(textlist);
396
4.98k
  MagickFreeMemory(text);
397
4.98k
  return(status);
398
4.98k
}
399

400
#if defined(HasTTF)
401
/*
402
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403
%                                                                             %
404
%                                                                             %
405
%                                                                             %
406
+   E n c o d e S J I S                                                       %
407
%                                                                             %
408
%                                                                             %
409
%                                                                             %
410
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411
%
412
%  EncodeSJIS() converts an ASCII text string to 2-bytes per character code
413
%  (like UCS-2).  Returns the translated codes and the character count.
414
%  Characters under 0x7f are just copied, characters over 0x80 are tied with
415
%  the next character.
416
%
417
%  Katsutoshi Shibuya contributed this method.
418
%
419
%  The format of the EncodeSJIS function is:
420
%
421
%      encoding=EncodeSJIS(const char *text,size_t count)
422
%
423
%  A description of each parameter follows:
424
%
425
%    o encoding:  EncodeSJIS() returns a pointer to an unsigned short
426
%      array representing the encoded version of the ASCII string.
427
%
428
%    o text: The text.
429
%
430
%    o count: return the number of characters generated by the encoding.
431
%
432
%
433
*/
434
435
static int GetOneCharacter(const unsigned char *text,size_t *length)
436
0
{
437
0
  unsigned int
438
0
    c;
439
440
0
  if (*length < 1)
441
0
    return(-1);
442
0
  c=text[0];
443
0
  if (!(c & 0x80))
444
0
    {
445
0
      *length=1;
446
0
      return((int) c);
447
0
    }
448
0
  if (*length < 2)
449
0
    {
450
0
      *length=0;
451
0
      return(-1);
452
0
    }
453
0
  *length=2;
454
0
  c=((int) (text[0]) << 8);
455
0
  c|=text[1];
456
0
  return((int) c);
457
0
}
458
459
static magick_code_point_t *EncodeSJIS(const char *text,size_t *count)
460
0
{
461
0
  int
462
0
    c;
463
464
0
  register const char
465
0
    *p;
466
467
0
  register magick_code_point_t
468
0
    *q;
469
470
0
  size_t
471
0
    length;
472
473
0
  magick_code_point_t
474
0
    *encoding;
475
476
0
  *count=0;
477
0
  if ((text == (char *) NULL) || (*text == '\0'))
478
0
    return((magick_code_point_t *) NULL);
479
0
  encoding=MagickAllocateArray(magick_code_point_t *,
480
0
                               (strlen(text)+MaxTextExtent),
481
0
                               sizeof(magick_code_point_t));
482
0
  if (encoding == (magick_code_point_t *) NULL)
483
0
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
484
0
      UnableToConvertText);
485
0
  q=encoding;
486
0
  for (p=text; *p != '\0'; p+=length)
487
0
  {
488
0
    length=strlen(p);
489
0
    c=GetOneCharacter((const unsigned char *) p,&length);
490
0
    if (c < 0)
491
0
      {
492
0
        q=encoding;
493
0
        for (p=text; *p != '\0'; p++)
494
0
          *q++=(unsigned char) *p;
495
0
        break;
496
0
      }
497
0
    *q=(magick_code_point_t) c;
498
0
    q++;
499
0
  }
500
0
  *count=q-encoding;
501
0
  return(encoding);
502
0
}
503

504
/*
505
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506
%                                                                             %
507
%                                                                             %
508
%                                                                             %
509
+   E n c o d e T e x t                                                       %
510
%                                                                             %
511
%                                                                             %
512
%                                                                             %
513
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514
%
515
%  EncodeText() converts an ASCII text string to wide text and returns the
516
%  translation and the character count.
517
%
518
%  The format of the EncodeText function is:
519
%
520
%      encoding=EncodeText(const char *text,size_t count)
521
%
522
%  A description of each parameter follows:
523
%
524
%    o encoding:  EncodeText() returns a pointer to an unsigned short array
525
%      array representing the encoded version of the ASCII string.
526
%
527
%    o text: The text.
528
%
529
%    o count: return the number of characters generated by the encoding.
530
%
531
%
532
*/
533
static magick_code_point_t *EncodeText(const char *text,size_t *count)
534
0
{
535
0
  register const char
536
0
    *p;
537
538
0
  register magick_code_point_t
539
0
    *q;
540
541
0
  magick_code_point_t
542
0
    *encoding;
543
544
0
  *count=0;
545
0
  if ((text == (char *) NULL) || (*text == '\0'))
546
0
    return((magick_code_point_t *) NULL);
547
0
  encoding=MagickAllocateArray(magick_code_point_t *,
548
0
                               (strlen(text)+MaxTextExtent),
549
0
                               sizeof(magick_code_point_t));
550
0
  if (encoding == (magick_code_point_t *) NULL)
551
0
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
552
0
      UnableToConvertText);
553
0
  q=encoding;
554
0
  for (p=text; *p != '\0'; p++)
555
0
    *q++=(unsigned char) *p;
556
0
  *count=q-encoding;
557
0
  return(encoding);
558
0
}
559

560
/*
561
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562
%                                                                             %
563
%                                                                             %
564
%                                                                             %
565
+   E n c o d e U n i c o d e                                                 %
566
%                                                                             %
567
%                                                                             %
568
%                                                                             %
569
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570
%
571
%  EncodeUnicode() converts an ASCII text string to Unicode and returns the
572
%  Unicode translation and the character count.  Characters under 0x7f are
573
%  just copied, characters over 0x80 are tied with the next character.
574
%
575
%  The format of the EncodeUnicode function is:
576
%
577
%      unicode=EncodeUnicode(const unsigned char *text,size_t count)
578
%
579
%  A description of each parameter follows:
580
%
581
%    o unicode:  EncodeUnicode() returns a pointer to an unsigned short array
582
%      array representing the encoded version of the ASCII string.
583
%
584
%    o text: The text.
585
%
586
%    o count: return the number of characters generated by the encoding.
587
%
588
%
589
*/
590
static int GetUnicodeCharacter(const unsigned char *text,size_t *length)
591
0
{
592
0
  unsigned int
593
0
    c;
594
595
0
  if (*length < 1)
596
0
    return(-1);
597
0
  c=text[0];
598
0
  if (!(c & 0x80))
599
0
    {
600
0
      *length=1;
601
0
      return((int) c);
602
0
    }
603
0
  if ((*length < 2) || ((text[1] & 0xc0) != 0x80))
604
0
    {
605
0
      *length=0;
606
0
      return(-1);
607
0
    }
608
0
  if ((c & 0xe0) != 0xe0)
609
0
    {
610
0
      *length=2;
611
0
      c=(text[0] & 0x1f) << 6;
612
0
      c|=text[1] & 0x3f;
613
0
      return((int) c);
614
0
    }
615
0
  if ((*length < 3) || ((text[2] & 0xc0) != 0x80))
616
0
    {
617
0
      *length=0;
618
0
      return(-1);
619
0
    }
620
0
  if ((c & 0xf0) != 0xf0)
621
0
    {
622
0
      *length=3;
623
0
      c=(text[0] & 0xf) << 12;
624
0
      c|=(text[1] & 0x3f) << 6;
625
0
      c|=text[2] & 0x3f;
626
0
      return((int) c);
627
0
    }
628
0
  if ((*length < 4) || ((c & 0xf8) != 0xf0) || ((text[3] & 0xc0) != 0x80))
629
0
    {
630
0
      *length=0;
631
0
      return(-1);
632
0
    }
633
0
  *length=4;
634
0
  c=(text[0] & 0x7) << 18;
635
0
  c|=(text[1] & 0x3f) << 12;
636
0
  c|=(text[2] & 0x3f) << 6;
637
0
  c|=text[3] & 0x3f;
638
0
  return((int) c);
639
0
}
640
641
static magick_code_point_t *EncodeUnicode(const char *text,size_t *count)
642
0
{
643
0
  int
644
0
    c;
645
646
0
  register const char
647
0
    *p;
648
649
0
  register magick_code_point_t
650
0
    *q;
651
652
0
  size_t
653
0
    length;
654
655
0
  magick_code_point_t
656
0
    *unicode;
657
658
0
  *count=0;
659
0
  if ((text == (char *) NULL) || (*text == '\0'))
660
0
    return((magick_code_point_t *) NULL);
661
0
  unicode=MagickAllocateArray(magick_code_point_t *,
662
0
                              (strlen(text)+MaxTextExtent),
663
0
                              sizeof(magick_code_point_t));
664
0
  if (unicode == (magick_code_point_t *) NULL)
665
0
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
666
0
      UnableToConvertText);
667
0
  q=unicode;
668
0
  for (p=text; *p != '\0'; p+=length)
669
0
  {
670
0
    length=strlen(p);
671
0
    c=GetUnicodeCharacter((const unsigned char *) p,&length);
672
0
    if (c < 0)
673
0
      {
674
0
        q=unicode;
675
0
        for (p=text; *p != '\0'; p++)
676
0
          *q++=(unsigned char) *p;
677
0
        break;
678
0
      }
679
0
    *q=(magick_code_point_t) c;
680
0
    q++;
681
0
  }
682
0
  *count=q-unicode;
683
0
  return(unicode);
684
0
}
685

686
#endif /* HasTTF */
687
/*
688
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
689
%                                                                             %
690
%                                                                             %
691
%                                                                             %
692
%   G e t T y p e M e t r i c s                                               %
693
%                                                                             %
694
%                                                                             %
695
%                                                                             %
696
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
697
%
698
%  GetTypeMetrics() returns the following information for the specified font
699
%  and text:
700
%
701
%    o character width
702
%    o character height
703
%    o ascent
704
%    o descent
705
%    o text width
706
%    o text height
707
%    o maximum horizontal advance
708
%    o underline position
709
%    o underline thickness
710
%
711
%  The format of the GetTypeMetrics method is:
712
%
713
%      unsigned int GetTypeMetrics(Image *image,const DrawInfo *draw_info,
714
%        TypeMetric *metrics)
715
%
716
%  A description of each parameter follows:
717
%
718
%    o image: The image.
719
%
720
%    o draw_info: The draw info.
721
%
722
%    o metrics: Return the font metrics in this structure.
723
%
724
%
725
*/
726
MagickExport MagickPassFail GetTypeMetrics(Image *image,const DrawInfo *draw_info,
727
  TypeMetric *metrics)
728
42.6k
{
729
42.6k
  DrawInfo
730
42.6k
    *clone_info;
731
732
42.6k
  PointInfo
733
42.6k
    offset;
734
735
42.6k
  MagickPassFail
736
42.6k
    status;
737
738
42.6k
  assert(draw_info != (DrawInfo *) NULL);
739
42.6k
  assert(draw_info->text != (char *) NULL);
740
42.6k
  assert(draw_info->signature == MagickSignature);
741
42.6k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
742
42.6k
  clone_info->render=False;
743
42.6k
  (void) memset(metrics,0,sizeof(TypeMetric));
744
42.6k
  offset.x=0.0;
745
42.6k
  offset.y=0.0;
746
42.6k
  status=RenderType(image,clone_info,&offset,metrics);
747
42.6k
  DestroyDrawInfo(clone_info);
748
42.6k
  return(status);
749
42.6k
}
750

751
752
/*
753
  Find a single font family name in a comma-separated list; returns a pointer
754
  to where next search should start (i.e., to the terminating character), or null
755
  if not found.  Trims leading and trailing white space, and surrounding single
756
  quotes.
757
*/
758
static
759
char const *FindCommaDelimitedName
760
(
761
 char const *  pSearchStart, /*start search here*/
762
 char const ** ppStart,      /*return pointer to first character in found string*/
763
 char const ** ppEnd         /*return pointer to just past last character in found string*/
764
 )
765
29.5k
{ /*FindCommaDelimitedName*/
766
767
29.5k
  int c;
768
29.5k
  char const * pStart;
769
29.5k
  char const * pEnd;
770
29.5k
  char const * pNextSearchStart;
771
772
29.5k
  if ( pSearchStart == 0 )
773
0
    return(0);
774
775
79.8k
  for ( pStart = pSearchStart; (c = *pStart) && (isspace(c) || (c == ','));
776
50.3k
        pStart++ );  /*skip leading spaces and commas*/
777
29.5k
  if ( c == '\0' )
778
5.51k
    return(0);  /*didn't find anything!*/
779
780
2.48M
  for ( pEnd = pStart + 1; (c = *pEnd) && (c != ','); pEnd++ ); /*find terminating comma*/
781
24.0k
  pNextSearchStart = pEnd;
782
783
24.0k
  for ( ; isspace((int) pEnd[-1]); pEnd-- ); /*trim trailing space; we know there is a non-space character there*/
784
785
  /* trim off surrounding single quotes */
786
24.0k
  if ((*pStart == '\'') &&  (*pEnd == '\'') && ((pEnd-pStart) >= 3))
787
0
    {
788
0
      pStart++;
789
0
      pEnd--;
790
0
    }
791
792
24.0k
  *ppStart = pStart;
793
24.0k
  *ppEnd = pEnd;
794
24.0k
  return(pNextSearchStart);
795
796
29.5k
} /*FindCommaDelimitedName*/
797
798
799
/*
800
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801
%                                                                             %
802
%                                                                             %
803
%                                                                             %
804
+   R e n d e r T y p e                                                       %
805
%                                                                             %
806
%                                                                             %
807
%                                                                             %
808
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
809
%
810
%  Method RenderType renders text on the image.  It also returns the bounding
811
%  box of the text relative to the image.
812
%
813
%  The format of the RenderType method is:
814
%
815
%      unsigned int RenderType(Image *image,DrawInfo *draw_info,
816
%        const PointInfo *offset,TypeMetric *metrics)
817
%
818
%  A description of each parameter follows:
819
%
820
%    o status: Method RenderType returns True if the text is rendered on the
821
%      image, otherwise False.
822
%
823
%    o image: The image.
824
%
825
%    o draw_info: The draw info.
826
%
827
%    o offset: (x,y) location of text relative to image.
828
%
829
%    o metrics: bounding box of text.
830
%
831
%
832
*/
833
static MagickPassFail RenderType(Image *image,const DrawInfo *draw_info,
834
                                 const PointInfo *offset,TypeMetric *metrics)
835
43.8k
{
836
43.8k
  const TypeInfo
837
43.8k
    *type_info;
838
839
43.8k
  DrawInfo
840
43.8k
    *clone_info;
841
842
43.8k
  MagickPassFail
843
43.8k
    status;
844
845
43.8k
  char
846
43.8k
    OneFontFamilyName[2048]; /*special handling only if font name this long or less*/
847
848
43.8k
  char const *
849
43.8k
    pTheFoundFontFamilyName;
850
851
43.8k
  type_info=(const TypeInfo *) NULL;
852
43.8k
  if (draw_info->font != (char *) NULL)
853
18.9k
    {
854
18.9k
      if (*draw_info->font == '@')
855
208
        return(RenderFreetype(image,draw_info,(char *) NULL,offset,metrics));
856
18.7k
      if (*draw_info->font == '-')
857
276
        return(RenderX11(image,draw_info,offset,metrics));
858
18.4k
      type_info=GetTypeInfo(draw_info->font,&image->exception);
859
18.4k
      if (type_info == (const TypeInfo *) NULL)
860
14.6k
        if (IsAccessible(draw_info->font))
861
1.64k
          return(RenderFreetype(image,draw_info,(char *) NULL,offset,metrics));
862
18.4k
    }
863
864
  /* draw_info->family may be a comma-separated list of names ... */
865
41.7k
  pTheFoundFontFamilyName = draw_info->family;
866
41.7k
  if (type_info == (const TypeInfo *) NULL)
867
37.8k
    { /*type_info not yet found*/
868
869
      /* stay consistent with previous behavior unless font family contains comma(s) */
870
37.8k
      if (draw_info->family == 0 || (strchr(draw_info->family,',') == 0))
871
32.3k
        { /*null ptr, or no commas in string; preserve previous behavior*/
872
873
32.3k
          type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
874
32.3k
                                        draw_info->stretch,draw_info->weight,
875
32.3k
                                        &image->exception);
876
877
32.3k
        } /*null ptr, or no commas in string; preserve previous behavior*/
878
5.51k
      else
879
5.51k
        { /*process as font family list*/
880
881
5.51k
          char const * pNext = draw_info->family, * pStart = 0, * pEnd = 0;
882
29.5k
          while ((pNext = FindCommaDelimitedName(pNext,&pStart,&pEnd)) != 0)
883
24.0k
            { /*found a name*/
884
885
24.0k
              unsigned int NameLength = pEnd - pStart;
886
24.0k
              if  ( NameLength >= sizeof(OneFontFamilyName) )
887
210
                continue;
888
23.8k
              memcpy(OneFontFamilyName,pStart,NameLength);
889
23.8k
              OneFontFamilyName[NameLength] = '\0';
890
23.8k
              type_info = GetTypeInfoByFamily(OneFontFamilyName,
891
23.8k
                                              draw_info->style,
892
23.8k
                                              draw_info->stretch,
893
23.8k
                                              draw_info->weight,
894
23.8k
                                              &image->exception);
895
              /*do not allow font substitution*/
896
23.8k
              if ( type_info && (LocaleCompare(OneFontFamilyName,
897
0
                                               type_info->family) == 0) )
898
0
                {
899
0
                  pTheFoundFontFamilyName = OneFontFamilyName;
900
0
                  break;
901
0
                }
902
903
23.8k
            } /*found a name*/
904
905
5.51k
        } /*process as font family list*/
906
907
37.8k
    } /*type_info not yet found*/
908
909
  /*
910
    We may have performed font substitution.  If so (i.e., font family
911
    name does not match), try again assuming draw_info->family is
912
    actually a font name.  If we get a font name match, that will
913
    override the font substitution.
914
  */
915
41.7k
  if ((type_info == (const TypeInfo *) NULL)
916
3.84k
      || /*found font family, but ...*/
917
3.84k
      (pTheFoundFontFamilyName &&
918
202
       (LocaleCompare(pTheFoundFontFamilyName,type_info->family) != 0)))
919
38.0k
    {/*either not found, or different font family (probably font substitution)*/
920
921
      /* try to match a font name */
922
38.0k
      const TypeInfo *type_info2 = 0;
923
38.0k
      if (((type_info2 = GetTypeInfo(pTheFoundFontFamilyName,
924
38.0k
                                     &image->exception))
925
38.0k
           == (const TypeInfo *) NULL)
926
26.8k
          && (pTheFoundFontFamilyName != 0)
927
26.8k
          && strlen(pTheFoundFontFamilyName) < sizeof(OneFontFamilyName))
928
26.7k
        {/*change ' ' to '-' and try again*/
929
930
          /*
931
            Change blanks to hyphens (i.e. make it look like a font
932
            name vs. font family).  Will only do this for font names
933
            sizeof(OneFontFamilyName) long or less.
934
          */
935
26.7k
          char FontNameWithHyphens[sizeof(OneFontFamilyName)];
936
26.7k
          char *pWithHyphens = FontNameWithHyphens;
937
26.7k
          char c;
938
26.7k
          const char *pFound;
939
26.7k
          for (pFound = pTheFoundFontFamilyName;
940
470k
               (*pWithHyphens = (((c = *pFound) != ' ') ? c : '-'));
941
443k
               pFound++, pWithHyphens++);
942
26.7k
          type_info2 = GetTypeInfo(FontNameWithHyphens,&image->exception);
943
944
26.7k
        } /*change ' ' to '-' and try again*/
945
946
38.0k
      if  ( type_info2 != (const TypeInfo *) NULL )
947
11.1k
        type_info = type_info2;
948
949
38.0k
    } /*either not found, or different font family (probably font substitution)*/
950
951
41.7k
  if (type_info == (const TypeInfo *) NULL)
952
26.6k
    return(RenderPostscript(image,draw_info,offset,metrics));
953
15.0k
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
954
15.0k
  if (type_info->glyphs != (char *) NULL)
955
0
    (void) CloneString(&clone_info->font,type_info->glyphs);
956
15.0k
  status=RenderFreetype(image,clone_info,type_info->encoding,offset,metrics);
957
15.0k
  DestroyDrawInfo(clone_info);
958
15.0k
  return(status);
959
41.7k
}
960

961
/*
962
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
963
%                                                                             %
964
%                                                                             %
965
%                                                                             %
966
+   R e n d e r F r e e t y p e                                               %
967
%                                                                             %
968
%                                                                             %
969
%                                                                             %
970
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
971
%
972
%  Method RenderFreetype renders text on the image with a Truetype font.  It
973
%  also returns the bounding box of the text relative to the image.
974
%
975
%  The format of the RenderFreetype method is:
976
%
977
%      unsigned int RenderFreetype(Image *image,DrawInfo *draw_info,
978
%        const char *encoding,const PointInfo *offset,TypeMetric *metrics)
979
%
980
%  A description of each parameter follows:
981
%
982
%    o status: Method RenderFreetype returns True if the text is rendered on the
983
%      image, otherwise False.
984
%
985
%    o image: The image.
986
%
987
%    o draw_info: The draw info.
988
%
989
%    o encoding: The font encoding.
990
%
991
%    o offset: (x,y) location of text relative to image.
992
%
993
%    o metrics: bounding box of text.
994
%
995
%
996
*/
997
998
#if defined(HasTTF)
999
static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
1000
  DrawInfo *draw_info)
1001
0
{
1002
0
  AffineMatrix
1003
0
    affine;
1004
1005
0
  char
1006
0
    path[MaxTextExtent];
1007
1008
0
  affine=draw_info->affine;
1009
0
  FormatString(path,"C%g,%g %g,%g %g,%g",affine.tx+p->x/64.0,
1010
0
    affine.ty-p->y/64.0,affine.tx+q->x/64.0,affine.ty-q->y/64.0,
1011
0
    affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1012
0
  (void) ConcatenateString(&draw_info->primitive,path);
1013
0
  return(0);
1014
0
}
1015
1016
static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
1017
0
{
1018
0
  AffineMatrix
1019
0
    affine;
1020
1021
0
  char
1022
0
    path[MaxTextExtent];
1023
1024
0
  affine=draw_info->affine;
1025
0
  FormatString(path,"L%g,%g",affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1026
0
  (void) ConcatenateString(&draw_info->primitive,path);
1027
0
  return(0);
1028
0
}
1029
1030
static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
1031
0
{
1032
0
  AffineMatrix
1033
0
    affine;
1034
1035
0
  char
1036
0
    path[MaxTextExtent];
1037
1038
0
  affine=draw_info->affine;
1039
0
  FormatString(path,"M%g,%g",affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1040
0
  (void) ConcatenateString(&draw_info->primitive,path);
1041
0
  return(0);
1042
0
}
1043
1044
static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
1045
  DrawInfo *draw_info)
1046
0
{
1047
0
  AffineMatrix
1048
0
    affine;
1049
1050
0
  char
1051
0
    path[MaxTextExtent];
1052
1053
0
  affine=draw_info->affine;
1054
0
  FormatString(path,"Q%g,%g %g,%g",affine.tx+control->x/64.0,
1055
0
    affine.ty-control->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0);
1056
0
  (void) ConcatenateString(&draw_info->primitive,path);
1057
0
  return(0);
1058
0
}
1059
1060
static MagickPassFail RenderFreetype(Image *image,const DrawInfo *draw_info,
1061
  const char *encoding,const PointInfo *offset,TypeMetric *metrics)
1062
16.8k
{
1063
16.8k
  typedef struct _GlyphInfo
1064
16.8k
  {
1065
16.8k
    FT_UInt
1066
16.8k
      id;
1067
1068
16.8k
    FT_Vector
1069
16.8k
      origin;
1070
1071
16.8k
    FT_Glyph
1072
16.8k
      image;
1073
16.8k
  } GlyphInfo;
1074
1075
16.8k
  double
1076
16.8k
    opacity;
1077
1078
16.8k
  DrawInfo
1079
16.8k
    *clone_info;
1080
1081
16.8k
  FT_BBox
1082
16.8k
    bounds;
1083
1084
16.8k
  FT_BitmapGlyph
1085
16.8k
    bitmap;
1086
1087
16.8k
  FT_Encoding
1088
16.8k
    encoding_type;
1089
1090
16.8k
  FT_Error
1091
16.8k
    ft_status;
1092
1093
16.8k
  FT_Face
1094
16.8k
    face;
1095
1096
16.8k
  FT_Library
1097
16.8k
    library;
1098
1099
16.8k
  FT_Matrix
1100
16.8k
    affine;
1101
1102
16.8k
  FT_Vector
1103
16.8k
    origin;
1104
1105
16.8k
  GlyphInfo
1106
16.8k
    glyph,
1107
16.8k
    last_glyph;
1108
1109
16.8k
  Image
1110
16.8k
    *pattern;
1111
1112
16.8k
  long
1113
16.8k
    y;
1114
1115
16.8k
  PixelPacket
1116
16.8k
    fill_color;
1117
1118
16.8k
  PointInfo
1119
16.8k
    point,
1120
16.8k
    resolution;
1121
1122
16.8k
  register long
1123
16.8k
    i,
1124
16.8k
    x;
1125
1126
16.8k
  register PixelPacket
1127
16.8k
    *q;
1128
1129
16.8k
  register unsigned char
1130
16.8k
    *p;
1131
1132
16.8k
  size_t
1133
16.8k
    length = 0;
1134
1135
16.8k
  static FT_Outline_Funcs
1136
16.8k
    OutlineMethods =
1137
16.8k
    {
1138
16.8k
      (FT_Outline_MoveTo_Func) TraceMoveTo,
1139
16.8k
      (FT_Outline_LineTo_Func) TraceLineTo,
1140
16.8k
      (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
1141
16.8k
      (FT_Outline_CubicTo_Func) TraceCubicBezier,
1142
16.8k
      0, 0
1143
16.8k
    };
1144
1145
16.8k
  MagickBool
1146
16.8k
    active;
1147
1148
16.8k
  magick_code_point_t
1149
16.8k
    *text;
1150
1151
16.8k
  MagickPassFail
1152
16.8k
    status=MagickPass;
1153
1154
16.8k
  if (draw_info->font == (char *) NULL)
1155
10.1k
    ThrowBinaryException(TypeError,FontNotSpecified,image->filename);
1156
1157
6.77k
  glyph.image=(FT_Glyph) 0;
1158
6.77k
  last_glyph.image=(FT_Glyph) 0;
1159
1160
  /*
1161
    Initialize Truetype library.
1162
  */
1163
6.77k
  ft_status=FT_Init_FreeType(&library);
1164
6.77k
  if (ft_status)
1165
0
    ThrowBinaryException(TypeError,UnableToInitializeFreetypeLibrary,
1166
6.77k
                         draw_info->font);
1167
6.77k
  if (*draw_info->font != '@')
1168
6.57k
    ft_status=FT_New_Face(library,draw_info->font,0,&face);
1169
208
  else
1170
208
    ft_status=FT_New_Face(library,draw_info->font+1,0,&face);
1171
6.77k
  if (ft_status != 0)
1172
6.77k
    {
1173
6.77k
      (void) FT_Done_FreeType(library);
1174
6.77k
      ThrowBinaryException(TypeError,UnableToReadFont,draw_info->font)
1175
6.77k
    }
1176
  /*
1177
    Select a charmap
1178
  */
1179
0
  if (face->num_charmaps != 0)
1180
0
    /* ft_status= */ (void) FT_Set_Charmap(face,face->charmaps[0]);
1181
0
  encoding_type=ft_encoding_unicode;
1182
0
  ft_status=FT_Select_Charmap(face,encoding_type);
1183
0
  if (ft_status != 0)
1184
0
    {
1185
0
      encoding_type=ft_encoding_none;
1186
0
      /* ft_status= */ (void) FT_Select_Charmap(face,encoding_type);
1187
0
    }
1188
0
  if (encoding != (char *) NULL)
1189
0
    {
1190
0
      if (LocaleCompare(encoding,"AdobeCustom") == 0)
1191
0
        encoding_type=ft_encoding_adobe_custom;
1192
0
      if (LocaleCompare(encoding,"AdobeExpert") == 0)
1193
0
        encoding_type=ft_encoding_adobe_expert;
1194
0
      if (LocaleCompare(encoding,"AdobeStandard") == 0)
1195
0
        encoding_type=ft_encoding_adobe_standard;
1196
0
      if (LocaleCompare(encoding,"AppleRoman") == 0)
1197
0
        encoding_type=ft_encoding_apple_roman;
1198
0
      if (LocaleCompare(encoding,"BIG5") == 0)
1199
0
        encoding_type=ft_encoding_big5;
1200
0
      if (LocaleCompare(encoding,"GB2312") == 0)
1201
0
        encoding_type=ft_encoding_gb2312;
1202
0
#if defined(ft_encoding_johab)
1203
0
      if (LocaleCompare(encoding,"Johab") == 0)
1204
0
        encoding_type=ft_encoding_johab;
1205
0
#endif
1206
0
#if defined(ft_encoding_latin_1)
1207
0
      if (LocaleCompare(encoding,"Latin-1") == 0)
1208
0
        encoding_type=ft_encoding_latin_1;
1209
0
#endif
1210
0
#if defined(ft_encoding_latin_2)
1211
0
      if (LocaleCompare(encoding,"Latin-2") == 0)
1212
0
        encoding_type=ft_encoding_latin_2;
1213
0
#endif
1214
0
      if (LocaleCompare(encoding,"None") == 0)
1215
0
        encoding_type=ft_encoding_none;
1216
0
      if (LocaleCompare(encoding,"SJIScode") == 0)
1217
0
        encoding_type=ft_encoding_sjis;
1218
0
      if (LocaleCompare(encoding,"Symbol") == 0)
1219
0
        encoding_type=ft_encoding_symbol;
1220
0
      if (LocaleCompare(encoding,"Unicode") == 0)
1221
0
        encoding_type=ft_encoding_unicode;
1222
0
      if (LocaleCompare(encoding,"Wansung") == 0)
1223
0
        encoding_type=ft_encoding_wansung;
1224
0
      ft_status=FT_Select_Charmap(face,encoding_type);
1225
0
      if (ft_status != 0)
1226
0
        ThrowBinaryException(TypeError,UnrecognizedFontEncoding,encoding);
1227
0
    }
1228
  /*
1229
    Set text size.
1230
  */
1231
0
  resolution.x=72.0;
1232
0
  resolution.y=72.0;
1233
0
  if (draw_info->density != (char *) NULL)
1234
0
    {
1235
0
      i=GetMagickDimension(draw_info->density,&resolution.x,&resolution.y,NULL,NULL);
1236
0
      if (i != 2)
1237
0
        resolution.y=resolution.x;
1238
0
    }
1239
0
  (void) FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
1240
0
    (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
1241
0
    (FT_UInt) resolution.y);
1242
0
  metrics->pixels_per_em.x=face->size->metrics.x_ppem;
1243
0
  metrics->pixels_per_em.y=face->size->metrics.y_ppem;
1244
0
  metrics->ascent=(double) face->size->metrics.ascender/64.0;
1245
0
  metrics->descent=(double) face->size->metrics.descender/64.0;
1246
0
  metrics->width=0;
1247
0
  metrics->height=(double) face->size->metrics.height/64.0;
1248
0
  metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
1249
0
  metrics->bounds.x1=0.0;
1250
0
  metrics->bounds.y1=metrics->descent;
1251
0
  metrics->bounds.x2=metrics->ascent+metrics->descent;
1252
0
  metrics->bounds.y2=metrics->ascent+metrics->descent;
1253
0
  metrics->underline_position=face->underline_position/64.0;
1254
0
  metrics->underline_thickness=face->underline_thickness/64.0;
1255
1256
  /*
1257
    If the user-provided text string is NULL or empty, then nothing
1258
    more to do.
1259
  */
1260
0
  if ((draw_info->text == NULL) || (draw_info->text[0] == '\0'))
1261
0
    {
1262
0
      (void) FT_Done_Face(face);
1263
0
      (void) FT_Done_FreeType(library);
1264
0
      return status;
1265
0
    }
1266
1267
  /*
1268
    Convert text to 4-byte format (supporting up to 21 code point
1269
    bits) as prescribed by the encoding.
1270
  */
1271
0
  switch (encoding_type)
1272
0
  {
1273
0
    case ft_encoding_sjis:
1274
0
    {
1275
0
      text=EncodeSJIS(draw_info->text,&length);
1276
0
      break;
1277
0
    }
1278
0
    case ft_encoding_unicode:
1279
0
    {
1280
0
      text=EncodeUnicode(draw_info->text,&length);
1281
0
      break;
1282
0
    }
1283
0
    default:
1284
0
    {
1285
0
      if (draw_info->encoding != (char *) NULL)
1286
0
        {
1287
0
          if (LocaleCompare(draw_info->encoding,"SJIS") == 0)
1288
0
            {
1289
0
              text=EncodeSJIS(draw_info->text,&length);
1290
0
              break;
1291
0
            }
1292
0
          if ((LocaleCompare(draw_info->encoding,"UTF-8") == 0) ||
1293
0
              (encoding_type != ft_encoding_none))
1294
0
            {
1295
0
              text=EncodeUnicode(draw_info->text,&length);
1296
0
              break;
1297
0
            }
1298
0
        }
1299
0
      text=EncodeText(draw_info->text,&length);
1300
0
      break;
1301
0
    }
1302
0
  }
1303
0
  if (text == (magick_code_point_t *) NULL)
1304
0
    {
1305
0
      (void) FT_Done_Face(face);
1306
0
      (void) FT_Done_FreeType(library);
1307
0
      (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1308
0
                            "Text encoding failed: encoding_type=%ld "
1309
0
                            "draw_info->encoding=\"%s\" draw_info->text=\"%s\" length=%ld",
1310
0
                            (long) encoding_type,
1311
0
                            (draw_info->encoding ? draw_info->encoding : "(null)"),
1312
0
                            (draw_info->text ? draw_info->text : "(null)"),
1313
0
                            (long) length);
1314
0
      ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
1315
0
        draw_info->font)
1316
0
    }
1317
  /*
1318
    Compute bounding box.
1319
  */
1320
0
  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1321
0
    "Font %.1024s; font-encoding %.1024s; text-encoding %.1024s; pointsize %g",
1322
0
    draw_info->font != (char *) NULL ? draw_info->font : "none",
1323
0
    encoding != (char *) NULL ? encoding : "none",
1324
0
    draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
1325
0
    draw_info->pointsize);
1326
0
  glyph.id=0;
1327
0
  last_glyph.id=0;
1328
0
  origin.x=0;
1329
0
  origin.y=0;
1330
0
  affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
1331
0
  affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
1332
0
  affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
1333
0
  affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
1334
0
  clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
1335
0
  (void) QueryColorDatabase("#000000ff",&clone_info->fill,&image->exception);
1336
0
  (void) CloneString(&clone_info->primitive,"path '");
1337
0
  pattern=draw_info->fill_pattern;
1338
0
  for (i=0; i < (long) length; i++)
1339
0
  {
1340
0
    glyph.id=FT_Get_Char_Index(face,text[i]);
1341
0
    if ((glyph.id != 0) && (last_glyph.id != 0) && FT_HAS_KERNING(face))
1342
0
      {
1343
0
        FT_Vector
1344
0
          kerning;
1345
1346
0
        (void) FT_Get_Kerning(face,last_glyph.id,glyph.id,ft_kerning_default,
1347
0
          &kerning);
1348
0
        origin.x+=kerning.x;
1349
0
      }
1350
0
    glyph.origin=origin;
1351
0
    glyph.image=0;
1352
0
    ft_status=FT_Load_Glyph(face,glyph.id,FT_LOAD_DEFAULT);
1353
0
    if (ft_status != False) /* 0 means success */
1354
0
      continue;
1355
0
    ft_status=FT_Get_Glyph(face->glyph,&glyph.image);
1356
0
    if (ft_status != False) /* 0 means success */
1357
0
      continue;
1358
#if 0
1359
    /*
1360
      Obtain glyph's control box. Usually faster than computing the
1361
      exact bounding box but may be slightly larger in some
1362
      situations.
1363
    */
1364
    (void) FT_Glyph_Get_CBox(glyph.image,FT_GLYPH_BBOX_SUBPIXELS,&bounds);
1365
#else
1366
    /*
1367
      Compute exact bounding box for scaled outline. If necessary, the
1368
      outline Bezier arcs are walked over to extract their extrema.
1369
    */
1370
0
    (void) FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->outline,&bounds);
1371
0
#endif
1372
0
    if ((i == 0) || (bounds.xMin < metrics->bounds.x1))
1373
0
      metrics->bounds.x1=bounds.xMin;
1374
0
    if ((i == 0) || (bounds.yMin < metrics->bounds.y1))
1375
0
      metrics->bounds.y1=bounds.yMin;
1376
0
    if ((i == 0) || (bounds.xMax > metrics->bounds.x2))
1377
0
      metrics->bounds.x2=bounds.xMax;
1378
0
    if ((i == 0) || (bounds.yMax > metrics->bounds.y2))
1379
0
      metrics->bounds.y2=bounds.yMax;
1380
0
    if (draw_info->render)
1381
0
      if ((draw_info->stroke.opacity != TransparentOpacity) ||
1382
0
          (draw_info->stroke_pattern != (Image *) NULL))
1383
0
        {
1384
          /*
1385
            Trace the glyph.
1386
          */
1387
0
          clone_info->affine.tx=glyph.origin.x/64.0;
1388
0
          clone_info->affine.ty=glyph.origin.y/64.0;
1389
0
          (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->outline,
1390
0
            &OutlineMethods,clone_info);
1391
0
        }
1392
0
    FT_Vector_Transform(&glyph.origin,&affine);
1393
0
    (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
1394
0
    if (draw_info->render)
1395
0
      {
1396
0
        status &= ModifyCache(image,&image->exception);
1397
0
        if ((draw_info->fill.opacity != TransparentOpacity) ||
1398
0
            (pattern != (Image *) NULL))
1399
0
          {
1400
            /*
1401
              Rasterize the glyph.
1402
            */
1403
0
            ft_status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
1404
0
              (FT_Vector *) NULL,True);
1405
0
            if (ft_status != False)
1406
0
              continue;
1407
0
            bitmap=(FT_BitmapGlyph) glyph.image;
1408
0
            image->storage_class=DirectClass;
1409
0
            if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1410
0
              {
1411
0
                point.x=offset->x+(origin.x >> 6);
1412
0
              }
1413
0
            else
1414
0
              {
1415
0
                point.x=offset->x+bitmap->left;
1416
0
              }
1417
0
            point.y=offset->y-bitmap->top;
1418
0
            p=bitmap->bitmap.buffer;
1419
            /* FIXME: OpenMP */
1420
0
            for (y=0; y < (long) bitmap->bitmap.rows; y++)
1421
0
            {
1422
0
              int pc = y * bitmap->bitmap.pitch;
1423
0
              int pcr = pc;
1424
0
              if ((ceil(point.y+y-0.5) < 0) ||
1425
0
                  (ceil(point.y+y-0.5) >= image->rows))
1426
0
                {
1427
0
                  continue;
1428
0
                }
1429
              /*
1430
                Try to get whole span.  May fail.
1431
              */
1432
0
              q=GetImagePixels(image,(long) ceil(point.x-0.5),
1433
0
                (long) ceil(point.y+y-0.5),bitmap->bitmap.width,1);
1434
0
              active=q != (PixelPacket *) NULL;
1435
0
              for (x=0; x < (long) bitmap->bitmap.width; x++, pc++)
1436
0
                {
1437
0
                  if (((long) ceil(point.x+x-0.5) < 0) ||
1438
0
                      ((unsigned long) ceil(point.x+x-0.5) >= image->columns))
1439
0
                    {
1440
0
                      if (active)
1441
0
                        q++;
1442
0
                      continue;
1443
0
                    }
1444
                  /* 8-bit gray-level pixmap */
1445
0
                  if (bitmap->bitmap.pixel_mode == ft_pixel_mode_grays)
1446
0
                    {
1447
0
                      if (draw_info->text_antialias)
1448
0
                        opacity=ScaleCharToQuantum((double) p[pc]);
1449
0
                      else
1450
0
                        opacity=(p[pc] < 127 ? OpaqueOpacity : TransparentOpacity);
1451
0
                    }
1452
                  /* 1-bit monochrome bitmap */
1453
0
                  else if (bitmap->bitmap.pixel_mode == ft_pixel_mode_mono)
1454
0
                    {
1455
0
                      opacity=((p[(x >> 3) + pcr] & (1 << (~x & 0x07))) ?
1456
0
                               TransparentOpacity : OpaqueOpacity);
1457
0
                    }
1458
0
                  else
1459
0
                    {
1460
0
                      continue; /* ignore it? */
1461
0
                    }
1462
0
                  fill_color=draw_info->fill;
1463
0
                  if (pattern != (Image *) NULL)
1464
0
                    {
1465
0
                      if (AcquireOnePixelByReference
1466
0
                          (pattern,&fill_color,
1467
0
                           (long) (point.x+x-pattern->tile_info.x) % pattern->columns,
1468
0
                           (long) (point.y+y-pattern->tile_info.y) % pattern->rows,
1469
0
                           &image->exception) == MagickFail)
1470
0
                        {
1471
0
                          status=MagickFail;
1472
0
                        }
1473
0
                    }
1474
                  /*
1475
                    If not full span, then get one pixel.
1476
                  */
1477
0
                  if (!active)
1478
0
                    q=GetImagePixels(image,(long) ceil(point.x+x-0.5),
1479
0
                                     (long) ceil(point.y+y-0.5),1,1);
1480
0
                  if (q == (PixelPacket *) NULL)
1481
0
                    {
1482
0
                      continue;
1483
0
                    }
1484
                  /*
1485
                    At this point, opacity is 0==transparent to MaxRGB==opaque, and represents an
1486
                    anti-aliasing edge blending value.  The computation below integrates in the
1487
                    fill color opacity, and converts the result to 0=opaque to MaxRGB=transparent.
1488
                  */
1489
0
                  opacity=MaxRGB-(opacity*(MaxRGB-fill_color.opacity)+/*round*/(MaxRGB>>1))/MaxRGB;
1490
0
                  AlphaCompositePixel(q,&fill_color,opacity,q,
1491
0
                                      image->matte ? q->opacity : OpaqueOpacity);
1492
0
                  if (!active)
1493
0
                    {
1494
                      /*
1495
                        Sync the one pixel
1496
                      */
1497
0
                      if (SyncImagePixels(image) != MagickPass)
1498
0
                        status=MagickFail;
1499
0
                    }
1500
0
                  else
1501
0
                    {
1502
0
                      q++;
1503
0
                    }
1504
0
                  if (status == MagickFail)
1505
0
                    break;
1506
0
                }
1507
              /*
1508
                Sync the full span
1509
              */
1510
0
              if (active)
1511
0
                if (SyncImagePixels(image) != MagickPass)
1512
0
                  status=MagickFail;
1513
0
              if (status == MagickFail)
1514
0
                break;
1515
0
            }
1516
0
          }
1517
0
      }
1518
0
    origin.x+=face->glyph->advance.x;
1519
0
    if (origin.x > metrics->width)
1520
0
      metrics->width=origin.x;
1521
0
    if (last_glyph.image != 0)
1522
0
      {
1523
0
        FT_Done_Glyph(last_glyph.image);
1524
0
        last_glyph.image=0;
1525
0
      }
1526
0
    last_glyph=glyph;
1527
0
  }
1528
0
  metrics->width/=64.0;
1529
0
  metrics->bounds.x1/=64.0;
1530
0
  metrics->bounds.y1/=64.0;
1531
0
  metrics->bounds.x2/=64.0;
1532
0
  metrics->bounds.y2/=64.0;
1533
0
  if ((status != MagickFail)&& (draw_info->render))
1534
0
    if ((draw_info->stroke.opacity != TransparentOpacity) ||
1535
0
        (draw_info->stroke_pattern != (Image *) NULL))
1536
0
      {
1537
        /*
1538
          Draw text stroke.
1539
        */
1540
0
        clone_info->affine.tx=offset->x;
1541
0
        clone_info->affine.ty=offset->y;
1542
0
        (void) ConcatenateString(&clone_info->primitive,"'");
1543
0
        (void) DrawImage(image,clone_info);
1544
0
      }
1545
0
  if (glyph.image != 0)
1546
0
    {
1547
0
      FT_Done_Glyph(glyph.image);
1548
0
      glyph.image=0;
1549
0
    }
1550
  /*
1551
    Free resources.
1552
  */
1553
0
  MagickFreeMemory(text);
1554
0
  DestroyDrawInfo(clone_info);
1555
0
  (void) FT_Done_Face(face);
1556
0
  (void) FT_Done_FreeType(library);
1557
0
  return(status);
1558
0
}
1559
#else
1560
static unsigned int RenderFreetype(Image *image,const DrawInfo *draw_info,
1561
  const char *encoding,const PointInfo *offset,
1562
  TypeMetric *metrics)
1563
{
1564
  ThrowBinaryException(MissingDelegateError,FreeTypeLibraryIsNotAvailable,
1565
                       draw_info->font);
1566
  ARG_NOT_USED(encoding);
1567
  ARG_NOT_USED(offset);
1568
  ARG_NOT_USED(metrics);
1569
}
1570
#endif
1571

1572
/*
1573
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1574
%                                                                             %
1575
%                                                                             %
1576
%                                                                             %
1577
+   R e n d e r P o s t s c r i p t                                           %
1578
%                                                                             %
1579
%                                                                             %
1580
%                                                                             %
1581
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1582
%
1583
%  Method RenderPostscript renders text on the image with a Postscript font.
1584
%  It also returns the bounding box of the text relative to the image.
1585
%
1586
%  The format of the RenderPostscript method is:
1587
%
1588
%      unsigned int RenderPostscript(Image *image,DrawInfo *draw_info,
1589
%        const PointInfo *offset,TypeMetric *metrics)
1590
%
1591
%  A description of each parameter follows:
1592
%
1593
%    o status: Method RenderPostscript returns True if the text is rendered on
1594
%      the image, otherwise False.
1595
%
1596
%    o image: The image.
1597
%
1598
%    o draw_info: The draw info.
1599
%
1600
%    o offset: (x,y) location of text relative to image.
1601
%
1602
%    o metrics: bounding box of text.
1603
%
1604
%
1605
*/
1606
1607
static char *EscapeParenthesis(const char *source)
1608
26.6k
{
1609
26.6k
  char
1610
26.6k
    *destination;
1611
1612
26.6k
  register char
1613
26.6k
    *q;
1614
1615
26.6k
  register const char
1616
26.6k
    *p;
1617
1618
26.6k
  size_t
1619
26.6k
    length;
1620
1621
26.6k
  assert(source != (const char *) NULL);
1622
1623
  /*
1624
    Use dry-run method to compute required string length.
1625
  */
1626
26.6k
  length=0;
1627
4.92M
  for (p=source; *p; p++)
1628
4.89M
    {
1629
4.89M
      if ((*p == '(') || (*p == ')'))
1630
10.2k
        length++;
1631
4.89M
      length++;
1632
4.89M
    }
1633
26.6k
  destination=MagickAllocateMemory(char *,length+1);
1634
26.6k
  if (destination == (char *) NULL)
1635
0
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
1636
26.6k
      UnableToEscapeString);
1637
26.6k
  *destination='\0';
1638
26.6k
  q=destination;
1639
4.92M
  for (p=source; *p; p++)
1640
4.89M
    {
1641
4.89M
      if ((*p == '(') || (*p == ')'))
1642
10.2k
        *q++= '\\';
1643
4.89M
      *q++=(*p);
1644
4.89M
    }
1645
26.6k
  *q=0;
1646
26.6k
  return(destination);
1647
26.6k
}
1648
1649
static MagickPassFail RenderPostscript(Image *image,const DrawInfo *draw_info,
1650
  const PointInfo *offset,TypeMetric *metrics)
1651
26.6k
{
1652
26.6k
  char
1653
26.6k
    filename[MaxTextExtent],
1654
26.6k
    geometry[MaxTextExtent],
1655
26.6k
    *text;
1656
1657
26.6k
  FILE
1658
26.6k
    *file;
1659
1660
26.6k
  Image
1661
26.6k
    *annotate_image,
1662
26.6k
    *pattern;
1663
1664
26.6k
  ImageInfo
1665
26.6k
    *clone_info;
1666
1667
26.6k
  long
1668
26.6k
    y;
1669
1670
26.6k
  PointInfo
1671
26.6k
    extent,
1672
26.6k
    point,
1673
26.6k
    resolution;
1674
1675
26.6k
  register long
1676
26.6k
    i,
1677
26.6k
    x;
1678
1679
26.6k
  register PixelPacket
1680
26.6k
    *q;
1681
1682
26.6k
  unsigned int
1683
26.6k
    identity;
1684
1685
  /*
1686
    Render label with a Postscript font.
1687
  */
1688
26.6k
  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1689
26.6k
    "Font %.1024s; pointsize %g",draw_info->font != (char *) NULL ?
1690
14.8k
    draw_info->font : "none",draw_info->pointsize);
1691
26.6k
  file=AcquireTemporaryFileStream(filename,BinaryFileIOMode);
1692
26.6k
  if (file == (FILE *) NULL)
1693
26.6k
    ThrowBinaryException(FileOpenError,UnableToCreateTemporaryFile,filename);
1694
26.6k
  (void) fprintf(file,"%%!PS-Adobe-3.0\n");
1695
26.6k
  (void) fprintf(file,"/ReencodeType\n");
1696
26.6k
  (void) fprintf(file,"{\n");
1697
26.6k
  (void) fprintf(file,"  findfont dup length\n");
1698
26.6k
  (void) fprintf(file,
1699
26.6k
    "  dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
1700
26.6k
  (void) fprintf(file,
1701
26.6k
    "  /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
1702
26.6k
  (void) fprintf(file,"} bind def\n");
1703
  /*
1704
    Sample to compute bounding box.
1705
  */
1706
26.6k
  identity=(draw_info->affine.sx == draw_info->affine.sy) &&
1707
22.3k
    (draw_info->affine.rx == 0.0) && (draw_info->affine.ry == 0.0);
1708
26.6k
  extent.x=0.0;
1709
26.6k
  extent.y=0.0;
1710
5.00M
  for (i=0; i <= (long) (strlen(draw_info->text)+2); i++)
1711
4.97M
  {
1712
4.97M
    point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
1713
4.97M
      draw_info->affine.ry*2.0*draw_info->pointsize);
1714
4.97M
    point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
1715
4.97M
      draw_info->affine.sy*2.0*draw_info->pointsize);
1716
4.97M
    if (point.x > extent.x)
1717
4.93M
      extent.x=point.x;
1718
4.97M
    if (point.y > extent.y)
1719
535k
      extent.y=point.y;
1720
4.97M
  }
1721
26.6k
  (void) fprintf(file,"%g %g moveto\n",identity ? 0.0 : extent.x/2.0,
1722
26.6k
    extent.y/2.0);
1723
26.6k
  (void) fprintf(file,"%g %g scale\n",draw_info->pointsize,
1724
26.6k
    draw_info->pointsize);
1725
26.6k
  if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0'))
1726
16.7k
    (void) fprintf(file,
1727
16.7k
      "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
1728
9.92k
  else
1729
9.92k
    (void) fprintf(file,
1730
9.92k
      "/%.1024s-ISO dup /%.1024s ReencodeType findfont setfont\n",
1731
9.92k
      draw_info->font,draw_info->font);
1732
26.6k
  (void) fprintf(file,"[%g %g %g %g 0 0] concat\n",draw_info->affine.sx,
1733
26.6k
    -draw_info->affine.rx,-draw_info->affine.ry,draw_info->affine.sy);
1734
26.6k
  text=EscapeParenthesis(draw_info->text);
1735
26.6k
  if (!identity)
1736
14.0k
    (void) fprintf(file,"(%.1024s) stringwidth pop -0.5 mul -0.5 rmoveto\n",
1737
14.0k
      text);
1738
26.6k
  (void) fprintf(file,"(%.1024s) show\n",text);
1739
26.6k
  MagickFreeMemory(text);
1740
26.6k
  (void) fprintf(file,"showpage\n");
1741
26.6k
  (void) fclose(file);
1742
26.6k
  FormatString(geometry,"%ldx%ld+0+0!",(long) ceil(extent.x-0.5),
1743
26.6k
    (long) ceil(extent.y-0.5));
1744
26.6k
  clone_info=CloneImageInfo((ImageInfo *) NULL);
1745
26.6k
  (void) FormatString(clone_info->filename,"ps:%.1024s",filename);
1746
26.6k
  (void) CloneString(&clone_info->page,geometry);
1747
26.6k
  if (draw_info->density != (char *) NULL)
1748
0
    (void) CloneString(&clone_info->density,draw_info->density);
1749
26.6k
  clone_info->antialias=draw_info->text_antialias;
1750
26.6k
  annotate_image=ReadImage(clone_info,&image->exception);
1751
26.6k
  if (image->exception.severity != UndefinedException)
1752
26.6k
    MagickError2(image->exception.severity,image->exception.reason,
1753
26.6k
      image->exception.description);
1754
26.6k
  DestroyImageInfo(clone_info);
1755
26.6k
  (void) LiberateTemporaryFile(filename);
1756
26.6k
  if (annotate_image == (Image *) NULL)
1757
26.6k
    return(False);
1758
0
  resolution.x=72.0;
1759
0
  resolution.y=72.0;
1760
0
  if (draw_info->density != (char *) NULL)
1761
0
    {
1762
0
      int
1763
0
        count;
1764
1765
0
      count=GetMagickDimension(draw_info->density,&resolution.x,&resolution.y,NULL,NULL);
1766
0
      if (count != 2)
1767
0
        resolution.y=resolution.x;
1768
0
    }
1769
0
  if (!identity)
1770
0
    TransformImage(&annotate_image,"0x0",(char *) NULL);
1771
0
  else
1772
0
    {
1773
0
      RectangleInfo
1774
0
        crop_info;
1775
1776
0
      crop_info=GetImageBoundingBox(annotate_image,&annotate_image->exception);
1777
0
      crop_info.height=(unsigned long) ceil((resolution.y/72.0)*
1778
0
        ExpandAffine(&draw_info->affine)*draw_info->pointsize-0.5);
1779
0
      crop_info.y=(long) ceil((resolution.y/72.0)*extent.y/8.0-0.5);
1780
0
      (void) FormatString(geometry,"%lux%lu%+ld%+ld",crop_info.width,
1781
0
        crop_info.height,crop_info.x,crop_info.y);
1782
0
      TransformImage(&annotate_image,geometry,(char *) NULL);
1783
0
    }
1784
0
  metrics->pixels_per_em.x=(resolution.y/72.0)*
1785
0
    ExpandAffine(&draw_info->affine)*draw_info->pointsize;
1786
0
  metrics->pixels_per_em.y=metrics->pixels_per_em.x;
1787
0
  metrics->ascent=metrics->pixels_per_em.x;
1788
0
  metrics->descent=metrics->pixels_per_em.y/-5.0;
1789
0
  metrics->width=annotate_image->columns/ExpandAffine(&draw_info->affine);
1790
0
  metrics->height=1.152*metrics->pixels_per_em.x;
1791
0
  metrics->max_advance=metrics->pixels_per_em.x;
1792
0
  metrics->bounds.x1=0.0;
1793
0
  metrics->bounds.y1=metrics->descent;
1794
0
  metrics->bounds.x2=metrics->ascent+metrics->descent;
1795
0
  metrics->bounds.y2=metrics->ascent+metrics->descent;
1796
0
  metrics->underline_position=(-2.0);
1797
0
  metrics->underline_thickness=1.0;
1798
0
  if (!draw_info->render)
1799
0
    {
1800
0
      DestroyImage(annotate_image);
1801
0
      return(True);
1802
0
    }
1803
0
  if (draw_info->fill.opacity != TransparentOpacity)
1804
0
    {
1805
0
      PixelPacket
1806
0
        fill_color;
1807
1808
      /*
1809
        Render fill color.
1810
      */
1811
0
      (void) SetImageType(annotate_image,TrueColorMatteType);
1812
0
      fill_color=draw_info->fill;
1813
0
      pattern=draw_info->fill_pattern;
1814
0
      for (y=0; y < (long) annotate_image->rows; y++)
1815
0
      {
1816
0
        q=GetImagePixels(annotate_image,0,y,annotate_image->columns,1);
1817
0
        if (q == (PixelPacket *) NULL)
1818
0
          break;
1819
0
        for (x=0; x < (long) annotate_image->columns; x++)
1820
0
        {
1821
0
          if (pattern != (Image *) NULL)
1822
0
            (void) AcquireOnePixelByReference(pattern,&fill_color,
1823
0
              (long) (x-pattern->tile_info.x) % pattern->columns,
1824
0
              (long) (y-pattern->tile_info.y) % pattern->rows,
1825
0
              &image->exception);
1826
0
          q->opacity=(Quantum) (MaxRGB-(((MaxRGB-(double)
1827
0
            PixelIntensityToQuantum(q))*(MaxRGB-fill_color.opacity))/
1828
0
            MaxRGB)+0.5);
1829
0
          q->red=fill_color.red;
1830
0
          q->green=fill_color.green;
1831
0
          q->blue=fill_color.blue;
1832
0
          q++;
1833
0
        }
1834
0
        if (!SyncImagePixels(annotate_image))
1835
0
          break;
1836
0
      }
1837
0
      (void) CompositeImage(image,OverCompositeOp,annotate_image,(long)
1838
0
        ceil(offset->x-0.5),(long) ceil(offset->y-(metrics->ascent+
1839
0
        metrics->descent)-0.5));
1840
0
    }
1841
0
  DestroyImage(annotate_image);
1842
0
  return(MagickPass);
1843
0
}
1844

1845
/*
1846
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1847
%                                                                             %
1848
%                                                                             %
1849
%                                                                             %
1850
+   R e n d e r X 1 1                                                         %
1851
%                                                                             %
1852
%                                                                             %
1853
%                                                                             %
1854
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1855
%
1856
%  Method RenderX11 renders text on the image with an X11 font.  It also
1857
%  returns the bounding box of the text relative to the image.
1858
%
1859
%  The format of the RenderX11 method is:
1860
%
1861
%      unsigned int RenderX11(Image *image,DrawInfo *draw_info,
1862
%        const PointInfo *offset,TypeMetric *metrics)
1863
%
1864
%  A description of each parameter follows:
1865
%
1866
%    o status: Method RenderX11 returns True if the text is rendered on the
1867
%      image, otherwise False.
1868
%
1869
%    o image: The image.
1870
%
1871
%    o draw_info: The draw info.
1872
%
1873
%    o offset: (x,y) location of text relative to image.
1874
%
1875
%    o metrics: bounding box of text.
1876
%
1877
%
1878
*/
1879
#if defined(HasX11)
1880
static MagickPassFail RenderX11(Image *image,const DrawInfo *draw_info,
1881
  const PointInfo *offset,TypeMetric *metrics)
1882
{
1883
  static DrawInfo
1884
    cache_info;
1885
1886
  static Display
1887
    *display = (Display *) NULL;
1888
1889
  static MagickXAnnotateInfo
1890
    annotate_info;
1891
1892
  static XFontStruct
1893
    *font_info;
1894
1895
  static MagickXPixelInfo
1896
    pixel;
1897
1898
  static MagickXResourceInfo
1899
    resource_info;
1900
1901
  static XrmDatabase
1902
    resource_database;
1903
1904
  static XStandardColormap
1905
    *map_info;
1906
1907
  static XVisualInfo
1908
    *visual_info;
1909
1910
  MagickPassFail
1911
    status;
1912
1913
  unsigned long
1914
    height,
1915
    width;
1916
1917
  if (display == (Display *) NULL)
1918
    {
1919
      const char
1920
        *client_name;
1921
1922
      /*
1923
        Open X server connection.
1924
      */
1925
      display=XOpenDisplay(draw_info->server_name);
1926
      if (display == (Display *) NULL)
1927
        ThrowBinaryException(XServerError,UnableToOpenXServer,
1928
          draw_info->server_name);
1929
      /*
1930
        Get user defaults from X resource database.
1931
      */
1932
      (void) XSetErrorHandler(MagickXError);
1933
      client_name=GetClientName();
1934
      resource_database=MagickXGetResourceDatabase(display,client_name);
1935
      MagickXGetResourceInfo(resource_database,client_name,&resource_info);
1936
      resource_info.close_server=False;
1937
      resource_info.colormap=PrivateColormap;
1938
      resource_info.font=AllocateString(draw_info->font);
1939
      resource_info.background_color=AllocateString("#ffffffffffff");
1940
      resource_info.foreground_color=AllocateString("#000000000000");
1941
      map_info=XAllocStandardColormap();
1942
      if (map_info == (XStandardColormap *) NULL)
1943
        ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
1944
          UnableToAllocateColormap);
1945
      /*
1946
        Initialize visual info.
1947
      */
1948
      visual_info=MagickXBestVisualInfo(display,map_info,&resource_info);
1949
      if (visual_info == (XVisualInfo *) NULL)
1950
        ThrowBinaryException(XServerError,UnableToGetVisual,
1951
          draw_info->server_name);
1952
      map_info->colormap=(Colormap) NULL;
1953
      pixel.pixels=(unsigned long *) NULL;
1954
      /*
1955
        Initialize Standard Colormap info.
1956
      */
1957
      MagickXGetMapInfo(visual_info,XDefaultColormap(display,visual_info->screen),
1958
        map_info);
1959
      MagickXGetPixelPacket(display,visual_info,map_info,&resource_info,
1960
        (Image *) NULL,&pixel);
1961
      pixel.annotate_context=XDefaultGC(display,visual_info->screen);
1962
      /*
1963
        Initialize font info.
1964
      */
1965
      font_info=MagickXBestFont(display,&resource_info,False);
1966
      if (font_info == (XFontStruct *) NULL)
1967
        ThrowBinaryException(XServerError,UnableToLoadFont,draw_info->font);
1968
      cache_info=(*draw_info);
1969
    }
1970
  /*
1971
    Initialize annotate info.
1972
  */
1973
  MagickXGetAnnotateInfo(&annotate_info);
1974
  annotate_info.stencil=ForegroundStencil;
1975
  if (cache_info.font != draw_info->font)
1976
    {
1977
      /*
1978
        Type name has changed.
1979
      */
1980
      (void) XFreeFont(display,font_info);
1981
      (void) CloneString(&resource_info.font,draw_info->font);
1982
      font_info=MagickXBestFont(display,&resource_info,False);
1983
      if (font_info == (XFontStruct *) NULL)
1984
        ThrowBinaryException(XServerError,UnableToLoadFont,draw_info->font);
1985
    }
1986
  (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
1987
    "Font %.1024s; pointsize %g",draw_info->font != (char *) NULL ?
1988
    draw_info->font : "none",draw_info->pointsize);
1989
  cache_info=(*draw_info);
1990
  annotate_info.font_info=font_info;
1991
  annotate_info.text=(char *) draw_info->text;
1992
  annotate_info.width=XTextWidth(font_info,draw_info->text,
1993
    (int) strlen(draw_info->text));
1994
  annotate_info.height=font_info->ascent+font_info->descent;
1995
  metrics->pixels_per_em.x=font_info->max_bounds.width;
1996
  metrics->pixels_per_em.y=font_info->max_bounds.width;
1997
  metrics->ascent=font_info->ascent;
1998
  metrics->descent=(-font_info->descent);
1999
  metrics->width=annotate_info.width/ExpandAffine(&draw_info->affine);
2000
  metrics->height=metrics->pixels_per_em.x+4;
2001
  metrics->max_advance=font_info->max_bounds.width;
2002
  metrics->bounds.x1=0.0;
2003
  metrics->bounds.y1=metrics->descent;
2004
  metrics->bounds.x2=metrics->ascent+metrics->descent;
2005
  metrics->bounds.y2=metrics->ascent+metrics->descent;
2006
  metrics->underline_position=(-2.0);
2007
  metrics->underline_thickness=1.0;
2008
  if (!draw_info->render)
2009
    return(MagickPass);
2010
  if (draw_info->fill.opacity == TransparentOpacity)
2011
    return(MagickPass);
2012
  /*
2013
    Render fill color.
2014
  */
2015
  width=annotate_info.width;
2016
  height=annotate_info.height;
2017
  if ((draw_info->affine.rx != 0.0) || (draw_info->affine.ry != 0.0))
2018
    {
2019
      if (((draw_info->affine.sx-draw_info->affine.sy) == 0.0) &&
2020
          ((draw_info->affine.rx+draw_info->affine.ry) == 0.0))
2021
        annotate_info.degrees=(180.0/MagickPI)*
2022
          atan2(draw_info->affine.rx,draw_info->affine.sx);
2023
    }
2024
  FormatString(annotate_info.geometry,"%lux%lu+%ld+%ld",width,height,
2025
    (long) ceil(offset->x-0.5),
2026
    (long) ceil(offset->y-metrics->ascent-metrics->descent-0.5));
2027
  pixel.pen_color.red=ScaleQuantumToShort(draw_info->fill.red);
2028
  pixel.pen_color.green=ScaleQuantumToShort(draw_info->fill.green);
2029
  pixel.pen_color.blue=ScaleQuantumToShort(draw_info->fill.blue);
2030
  status=MagickXAnnotateImage(display,&pixel,&annotate_info,image);
2031
  if (status == 0)
2032
    ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
2033
      UnableToAnnotateImage);
2034
  return(MagickPass);
2035
}
2036
#else
2037
static MagickPassFail RenderX11(Image *image,const DrawInfo *draw_info,
2038
  const PointInfo *offset,TypeMetric *metrics)
2039
276
{
2040
276
  ARG_NOT_USED(offset);
2041
276
  ARG_NOT_USED(metrics);
2042
276
  ThrowBinaryException(MissingDelegateError,XWindowLibraryIsNotAvailable,
2043
276
    draw_info->font);
2044
0
}
2045
#endif