]> git.imager.perl.org - imager.git/blob - FT2/freetyp2.c
6431ad2bbb1dd786d561d147c9d1f297712ed395
[imager.git] / FT2 / freetyp2.c
1 /*
2 =head1 NAME
3
4 freetyp2.c - font support via the FreeType library version 2.
5
6 =head1 SYNOPSIS
7
8   if (!i_ft2_init()) { error }
9   FT2_Fonthandle *font;
10   font = i_ft2_new(name, index);
11   if (!i_ft2_setdpi(font, xdpi, ydpi)) { error }
12   if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
13   double matrix[6];
14   if (!i_ft2_settransform(font, matrix)) { error }
15   i_img_dim bbox[BOUNDING_BOX_COUNT];
16   if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
17   i_img *im = ...;
18   i_color cl;
19   if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align,
20                   aa)) { error }
21   if (!i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, length,
22                 align, aa)) { error }
23   i_ft2_destroy(font);
24
25 =head1 DESCRIPTION
26
27 Implements Imager font support using the FreeType2 library.
28
29 The FreeType2 library understands several font file types, including
30 Truetype, Type1 and Windows FNT.
31
32 =over 
33
34 =cut
35 */
36
37 #include "imext.h"
38 #include "imft2.h"
39 #include <stdio.h>
40 #include <math.h>
41 #include <string.h>
42 #include <ft2build.h>
43 #include FT_FREETYPE_H
44 #ifdef FT_MULTIPLE_MASTERS_H
45 #ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
46 #define IM_FT2_MM
47 #include FT_MULTIPLE_MASTERS_H
48 #endif
49 #endif
50
51 static void ft2_push_message(int code);
52
53 static void ft2_final(void *);
54
55 static im_slot_t slot = -1;
56
57 typedef struct {
58   int initialized;
59   FT_Library library;
60   im_context_t ctx;
61 } ft2_state;
62
63 static ft2_state *
64 i_ft2_init(void);
65
66 static i_img_dim i_min(i_img_dim a, i_img_dim b);
67 static i_img_dim i_max(i_img_dim a, i_img_dim b);
68
69 int
70 i_ft2_version(int runtime, char *buf, size_t buf_size) {
71   char work[100];
72
73   i_clear_error();
74
75   if (buf_size == 0) {
76     i_push_error(0, "zero size buffer supplied");
77     return 0;
78   }
79   if (runtime) {
80     ft2_state *ft2;
81     /* initialized to work around a bug in FT2
82        http://lists.nongnu.org/archive/html/freetype-devel/2002-09/msg00058.html
83        Though I don't know why I still see this in 2.4.2
84      */
85     FT_Int major = 1, minor = 1, patch = 1;
86
87     if ((ft2 = i_ft2_init()) == NULL)
88       return 0;
89
90     FT_Library_Version(ft2->library, &major, &minor, &patch);
91     sprintf(work, "%d.%d.%d", (int)major, (int)minor, (int)patch);
92   }
93   else {
94     sprintf(work, "%d.%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
95   }
96   strncpy(buf, work, buf_size);
97   buf[buf_size-1] = '\0';
98
99   return 1;
100 }
101
102 void
103 i_ft2_start(void) {
104   if (slot == -1)
105     slot = im_context_slot_new(ft2_final);
106 }
107
108 /*
109 =item i_ft2_init(void)
110
111 Initializes the Freetype 2 library.
112
113 Returns ft2_state * on success or NULL on failure.
114
115 =cut
116 */
117
118 static ft2_state *
119 i_ft2_init(void) {
120   FT_Error error;
121   im_context_t ctx = im_get_context();
122   ft2_state *ft2 = im_context_slot_get(ctx, slot);
123
124   if (ft2 == NULL) {
125     ft2 = mymalloc(sizeof(ft2_state));
126     ft2->initialized = 0;
127     ft2->library = NULL;
128     ft2->ctx = ctx;
129     im_context_slot_set(ctx, slot, ft2);
130     mm_log((1, "created FT2 state %p for context %p\n", ft2, ctx));
131   }
132
133   i_clear_error();
134   if (!ft2->initialized) {
135     error = FT_Init_FreeType(&ft2->library);
136     if (error) {
137       ft2_push_message(error);
138       i_push_error(0, "Initializing Freetype2");
139       return NULL;
140     }
141     mm_log((1, "initialized FT2 state %p\n", ft2));
142
143     ft2->initialized = 1;
144   }
145
146   return ft2;
147 }
148
149 static void
150 ft2_final(void *state) {
151   ft2_state *ft2 = state;
152
153   if (ft2->initialized) {
154     mm_log((1, "finalizing FT2 state %p\n", state));
155     FT_Done_FreeType(ft2->library);
156     ft2->library = NULL;
157     ft2->initialized = 0;
158   }
159
160   mm_log((1, "freeing FT2 state %p\n", state));
161   myfree(state);
162 }
163
164 struct FT2_Fonthandle {
165   FT_Face face;
166   ft2_state *state;
167   int xdpi, ydpi;
168   int hint;
169   FT_Encoding encoding;
170
171   /* used to adjust so we can align the draw point to the top-left */
172   double matrix[6];
173
174 #ifdef IM_FT2_MM
175   /* Multiple master data if any */
176   int has_mm;
177   FT_Multi_Master mm;
178 #endif
179 };
180
181 /* the following is used to select a "best" encoding */
182 static struct enc_score {
183   FT_Encoding encoding;
184   int score;
185 } enc_scores[] =
186 {
187   /* the selections here are fairly arbitrary
188      ideally we need to give the user a list of encodings available
189      and a mechanism to choose one */
190   { ft_encoding_unicode,        10 },
191   { ft_encoding_sjis,            8 },
192   { ft_encoding_gb2312,          8 },
193   { ft_encoding_big5,            8 },
194   { ft_encoding_wansung,         8 },
195   { ft_encoding_johab,           8 },  
196   { ft_encoding_latin_2,         6 },
197   { ft_encoding_apple_roman,     6 },
198   { ft_encoding_adobe_standard,  6 },
199   { ft_encoding_adobe_expert,    6 },
200 };
201
202 /*
203 =item i_ft2_new(char *name, int index)
204
205 Creates a new font object, from the file given by I<name>.  I<index>
206 is the index of the font in a file with multiple fonts, where 0 is the
207 first font.
208
209 Return NULL on failure.
210
211 =cut
212 */
213
214 FT2_Fonthandle *
215 i_ft2_new(const char *name, int index) {
216   FT_Error error;
217   FT2_Fonthandle *result;
218   FT_Face face;
219   int i, j;
220   FT_Encoding encoding;
221   int score;
222   ft2_state *ft2;
223
224   mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
225
226   if ((ft2 = i_ft2_init()) == NULL)
227     return NULL;
228
229   i_clear_error();
230   error = FT_New_Face(ft2->library, name, index, &face);
231   if (error) {
232     ft2_push_message(error);
233     i_push_error(error, "Opening face");
234     mm_log((2, "error opening face '%s': %d\n", name, error));
235     return NULL;
236   }
237
238   encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
239   score = 0;
240   for (i = 0; i < face->num_charmaps; ++i) {
241     FT_Encoding enc_entry = face->charmaps[i]->encoding;
242     mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n",
243             enc_entry, face->charmaps[i]->platform_id,
244             face->charmaps[i]->encoding_id));
245     for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
246       if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
247         encoding = enc_entry;
248         score = enc_scores[j].score;
249         break;
250       }
251     }
252   }
253   FT_Select_Charmap(face, encoding);
254   mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding));
255
256   result = mymalloc(sizeof(FT2_Fonthandle));
257   result->face = face;
258   result->state = ft2;
259   result->xdpi = result->ydpi = 72;
260   result->encoding = encoding;
261
262   /* by default we disable hinting on a call to i_ft2_settransform()
263      if we don't do this, then the hinting can the untransformed text
264      to be a different size to the transformed text.
265      Obviously we have it initially enabled.
266   */
267   result->hint = 1; 
268
269   /* I originally forgot this:   :/ */
270   /*i_ft2_settransform(result, matrix); */
271   result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
272   result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
273
274 #ifdef IM_FT2_MM
275  {
276    FT_Multi_Master *mm = &result->mm;
277    int i;
278
279    if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0 
280        && (error = FT_Get_Multi_Master(face, mm)) == 0) {
281      mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs));
282      for (i = 0; i < mm->num_axis; ++i) {
283        mm_log((2, "  axis %d name %s range %ld - %ld\n", i, mm->axis[i].name,
284                (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum)));
285      }
286      result->has_mm = 1;
287    }
288    else {
289      mm_log((2, "No multiple masters\n"));
290      result->has_mm = 0;
291    }
292  }
293 #endif
294
295   return result;
296 }
297
298 /*
299 =item i_ft2_destroy(FT2_Fonthandle *handle)
300
301 Destroys a font object, which must have been the return value of
302 i_ft2_new().
303
304 =cut
305 */
306 void
307 i_ft2_destroy(FT2_Fonthandle *handle) {
308   FT_Done_Face(handle->face);
309   myfree(handle);
310 }
311
312 /*
313 =item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
314
315 Sets the resolution in dots per inch at which point sizes scaled, by
316 default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
317
318 Both xdpi and ydpi should be positive.
319
320 Return true on success.
321
322 =cut
323 */
324 int
325 i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
326   i_clear_error();
327   if (xdpi > 0 && ydpi > 0) {
328     handle->xdpi = xdpi;
329     handle->ydpi = ydpi;
330     return 1;
331   }
332   else {
333     i_push_error(0, "resolutions must be positive");
334     return 0;
335   }
336 }
337
338 /*
339 =item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
340
341 Retrieves the current horizontal and vertical resolutions at which
342 point sizes are scaled.
343
344 =cut
345 */
346 int
347 i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
348   *xdpi = handle->xdpi;
349   *ydpi = handle->ydpi;
350
351   return 1;
352 }
353
354 /*
355 =item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
356
357 Sets a transormation matrix for output.
358
359 This should be a 2 x 3 matrix like:
360
361  matrix[0]   matrix[1]   matrix[2]
362  matrix[3]   matrix[4]   matrix[5]
363
364 =cut
365 */
366 int
367 i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix) {
368   FT_Matrix m;
369   FT_Vector v;
370   int i;
371
372   m.xx = matrix[0] * 65536;
373   m.xy = matrix[1] * 65536;
374   v.x  = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
375   m.yx = matrix[3] * 65536;
376   m.yy = matrix[4] * 65536;
377   v.y  = matrix[5]; /* see just above */
378
379   FT_Set_Transform(handle->face, &m, &v);
380
381   for (i = 0; i < 6; ++i)
382     handle->matrix[i] = matrix[i];
383   handle->hint = 0;
384
385   return 1;
386 }
387
388 /*
389 =item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
390
391 If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
392
393 i_ft2_settransform() disables hinting to prevent distortions in
394 gradual text transformations.
395
396 =cut
397 */
398 int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
399   handle->hint = hinting;
400   return 1;
401 }
402
403 /*
404 =item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, i_img_dim *bbox)
405
406 Retrieves bounding box information for the font at the given 
407 character width and height.  This ignores the transformation matrix.
408
409 Returns non-zero on success.
410
411 =cut
412 */
413 int
414 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, 
415            char const *text, size_t len, i_img_dim *bbox, int utf8) {
416   FT_Error error;
417   i_img_dim width;
418   int index;
419   int first;
420   int ascent = 0, descent = 0;
421   int glyph_ascent, glyph_descent;
422   FT_Glyph_Metrics *gm;
423   int start = 0;
424   int loadFlags = FT_LOAD_DEFAULT;
425   int rightb = 0;
426
427   mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
428           handle, cheight, cwidth, text, len, bbox));
429
430   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
431                            handle->xdpi, handle->ydpi);
432   if (error) {
433     ft2_push_message(error);
434     i_push_error(0, "setting size");
435   }
436
437   if (!handle->hint)
438     loadFlags |= FT_LOAD_NO_HINTING;
439
440   first = 1;
441   width = 0;
442   while (len) {
443     unsigned long c;
444     if (utf8) {
445       c = i_utf8_advance(&text, &len);
446       if (c == ~0UL) {
447         i_push_error(0, "invalid UTF8 character");
448         return 0;
449       }
450     }
451     else {
452       c = (unsigned char)*text++;
453       --len;
454     }
455
456     index = FT_Get_Char_Index(handle->face, c);
457     error = FT_Load_Glyph(handle->face, index, loadFlags);
458     if (error) {
459       ft2_push_message(error);
460       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
461                     c, index);
462       return 0;
463     }
464     gm = &handle->face->glyph->metrics;
465     glyph_ascent = gm->horiBearingY / 64;
466     glyph_descent = glyph_ascent - gm->height/64;
467     if (first) {
468       start = gm->horiBearingX / 64;
469       /* handles -ve values properly */
470       ascent = glyph_ascent;
471       descent = glyph_descent;
472       first = 0;
473     }
474
475     if (glyph_ascent > ascent)
476       ascent = glyph_ascent;
477     if (glyph_descent < descent)
478       descent = glyph_descent;
479
480     width += gm->horiAdvance / 64;
481
482     if (len == 0) {
483       /* last character 
484        handle the case where the right the of the character overlaps the 
485        right*/
486       rightb = (gm->horiAdvance - gm->horiBearingX - gm->width)/64;
487       /*if (rightb > 0)
488         rightb = 0;*/
489     }
490   }
491
492   bbox[BBOX_NEG_WIDTH] = start;
493   bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
494   bbox[BBOX_POS_WIDTH] = width;
495   if (rightb < 0)
496     bbox[BBOX_POS_WIDTH] -= rightb;
497   bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
498   bbox[BBOX_DESCENT] = descent;
499   bbox[BBOX_ASCENT] = ascent;
500   bbox[BBOX_ADVANCE_WIDTH] = width;
501   bbox[BBOX_RIGHT_BEARING] = rightb;
502   mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
503
504   return BBOX_RIGHT_BEARING + 1;
505 }
506
507 /*
508 =item transform_box(FT2_FontHandle *handle, int bbox[4])
509
510 bbox contains coorinates of a the top-left and bottom-right of a bounding 
511 box relative to a point.
512
513 This is then transformed and the values in bbox[4] are the top-left
514 and bottom-right of the new bounding box.
515
516 This is meant to provide the bounding box of a transformed character
517 box.  The problem is that if the character was round and is rotated,
518 the real bounding box isn't going to be much different from the
519 original, but this function will return a _bigger_ bounding box.  I
520 suppose I could work my way through the glyph outline, but that's
521 too much hard work.
522
523 =cut
524 */
525 void ft2_transform_box(FT2_Fonthandle *handle, i_img_dim bbox[4]) {
526   double work[8];
527   double *matrix = handle->matrix;
528   
529   work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
530   work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
531   work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
532   work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
533   work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
534   work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
535   work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
536   work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
537
538   bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
539   bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
540   bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
541   bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
542 }
543
544 /*
545 =item expand_bounds(int bbox[4], int bbox2[4]) 
546
547 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
548 bounding box in bbox[] that encloses both.
549
550 =cut
551 */
552 static void expand_bounds(i_img_dim bbox[4], i_img_dim bbox2[4]) {
553   bbox[0] = i_min(bbox[0], bbox2[0]);
554   bbox[1] = i_min(bbox[1], bbox2[1]);
555   bbox[2] = i_max(bbox[2], bbox2[2]);
556   bbox[3] = i_max(bbox[3], bbox2[3]);
557 }
558
559 /*
560 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int vlayout, int utf8, i_img_dim *bbox)
561
562 Retrieves bounding box information for the font at the given 
563 character width and height.
564
565 This version finds the rectangular bounding box of the glyphs, with
566 the text as transformed by the transformation matrix.  As with
567 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
568 the topline to the top-left of the bounding box.  Unlike i_ft2_bbox()
569 this could be near the bottom left corner of the box.
570
571 (bbox[4], bbox[5]) is the offset to the start of the baseline.
572 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
573 end of the baseline.
574
575 Returns non-zero on success.
576
577 =cut
578 */
579 int
580 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, 
581            char const *text, size_t len, int vlayout, int utf8, i_img_dim *bbox) {
582   FT_Error error;
583   i_img_dim width;
584   int index;
585   int first;
586   i_img_dim ascent = 0, descent = 0;
587   int glyph_ascent, glyph_descent;
588   FT_Glyph_Metrics *gm;
589   i_img_dim work[4];
590   i_img_dim bounds[4];
591   double x = 0, y = 0;
592   int i;
593   FT_GlyphSlot slot;
594   int loadFlags = FT_LOAD_DEFAULT;
595
596   if (vlayout)
597     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
598   if (!handle->hint)
599     loadFlags |= FT_LOAD_NO_HINTING;
600
601   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
602                            handle->xdpi, handle->ydpi);
603   if (error) {
604     ft2_push_message(error);
605     i_push_error(0, "setting size");
606   }
607
608   first = 1;
609   width = 0;
610   while (len) {
611     unsigned long c;
612     if (utf8) {
613       c = i_utf8_advance(&text, &len);
614       if (c == ~0UL) {
615         i_push_error(0, "invalid UTF8 character");
616         return 0;
617       }
618     }
619     else {
620       c = (unsigned char)*text++;
621       --len;
622     }
623
624     index = FT_Get_Char_Index(handle->face, c);
625     error = FT_Load_Glyph(handle->face, index, loadFlags);
626     if (error) {
627       ft2_push_message(error);
628       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
629                     c, index);
630       return 0;
631     }
632     slot = handle->face->glyph; 
633     gm = &slot->metrics;
634
635     /* these probably don't mean much for vertical layouts */
636     glyph_ascent = gm->horiBearingY / 64;
637     glyph_descent = glyph_ascent - gm->height/64;
638     if (vlayout) {
639       work[0] = gm->vertBearingX;
640       work[1] = gm->vertBearingY;
641     }
642     else {
643       work[0] = gm->horiBearingX;
644       work[1] = gm->horiBearingY;
645     }
646     work[2] = gm->width  + work[0];
647     work[3] = work[1] - gm->height;
648     if (first) {
649       bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
650       bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
651       bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
652       bbox[5] /= 64;
653     }
654     ft2_transform_box(handle, work);
655     for (i = 0; i < 4; ++i)
656       work[i] /= 64;
657     work[0] += x;
658     work[1] += y;
659     work[2] += x;
660     work[3] += y;
661     if (first) {
662       for (i = 0; i < 4; ++i)
663         bounds[i] = work[i];
664       ascent = glyph_ascent;
665       descent = glyph_descent;
666       first = 0;
667     }
668     else {
669       expand_bounds(bounds, work);
670     }
671     x += slot->advance.x / 64;
672     y += slot->advance.y / 64;
673
674     if (glyph_ascent > ascent)
675       ascent = glyph_ascent;
676     if (glyph_descent > descent)
677       descent = glyph_descent;
678
679     if (len == 0) {
680       /* last character 
681        handle the case where the right the of the character overlaps the 
682        right*/
683       /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
684       if (rightb < 0)
685       width -= rightb / 64;*/
686     }
687   }
688
689   /* at this point bounds contains the bounds relative to the CP,
690      and x, y hold the final position relative to the CP */
691   /*bounds[0] -= x;
692   bounds[1] -= y;
693   bounds[2] -= x;
694   bounds[3] -= y;*/
695
696   bbox[0] = bounds[0];
697   bbox[1] = -bounds[3];
698   bbox[2] = bounds[2];
699   bbox[3] = -bounds[1];
700   bbox[6] = x;
701   bbox[7] = -y;
702
703   return 1;
704 }
705
706 static int
707 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
708
709 /*
710 =item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, size_t len, int align, int aa)
711
712 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given 
713 I<cheight> and I<cwidth>.
714
715 If align is 0, then the text is rendered with the top-left of the
716 first character at (I<tx>, I<ty>).  If align is non-zero then the text
717 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
718 characters.
719
720 If aa is non-zero then the text is anti-aliased.
721
722 Returns non-zero on success.
723
724 =cut
725 */
726 int
727 i_ft2_text(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, const i_color *cl,
728            double cheight, double cwidth, char const *text, size_t len,
729            int align, int aa, int vlayout, int utf8) {
730   FT_Error error;
731   int index;
732   FT_Glyph_Metrics *gm;
733   i_img_dim bbox[BOUNDING_BOX_COUNT];
734   FT_GlyphSlot slot;
735   int x, y;
736   unsigned char *bmp;
737   unsigned char map[256];
738   char last_mode = ft_pixel_mode_none; 
739   int last_grays = -1;
740   int loadFlags = FT_LOAD_DEFAULT;
741   i_render *render;
742
743   mm_log((1, "i_ft2_text(handle %p, im %p, tx %d, ty %d, cl %p, cheight %f, cwidth %f, text %p, len %d, align %d, aa %d)\n",
744           handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
745
746   if (vlayout) {
747     if (!FT_HAS_VERTICAL(handle->face)) {
748       i_push_error(0, "face has no vertical metrics");
749       return 0;
750     }
751     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
752   }
753   if (!handle->hint)
754     loadFlags |= FT_LOAD_NO_HINTING;
755
756   /* set the base-line based on the string ascent */
757   if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
758     return 0;
759
760   if (aa)
761     render = i_render_new(im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
762
763   if (!align) {
764     /* this may need adjustment */
765     tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
766     ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
767   }
768   while (len) {
769     unsigned long c;
770     if (utf8) {
771       c = i_utf8_advance(&text, &len);
772       if (c == ~0UL) {
773         i_push_error(0, "invalid UTF8 character");
774         return 0;
775       }
776     }
777     else {
778       c = (unsigned char)*text++;
779       --len;
780     }
781     
782     index = FT_Get_Char_Index(handle->face, c);
783     error = FT_Load_Glyph(handle->face, index, loadFlags);
784     if (error) {
785       ft2_push_message(error);
786       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
787                     c, index);
788       if (aa)
789         i_render_delete(render);
790       return 0;
791     }
792     slot = handle->face->glyph;
793     gm = &slot->metrics;
794
795     if (gm->width) {
796       error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
797       if (error) {
798         ft2_push_message(error);
799         i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
800       if (aa)
801         i_render_delete(render);
802         return 0;
803       }
804       if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
805         bmp = slot->bitmap.buffer;
806         for (y = 0; y < slot->bitmap.rows; ++y) {
807           int pos = 0;
808           int bit = 0x80;
809           for (x = 0; x < slot->bitmap.width; ++x) {
810             if (bmp[pos] & bit)
811               i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
812             
813             bit >>= 1;
814             if (bit == 0) {
815               bit = 0x80;
816               ++pos;
817             }
818           }
819           bmp += slot->bitmap.pitch;
820         }
821       }
822       else {
823         /* grey scale or something we can treat as greyscale */
824         /* we create a map to convert from the bitmap values to 0-255 */
825         if (last_mode != slot->bitmap.pixel_mode 
826             || last_grays != slot->bitmap.num_grays) {
827           if (!make_bmp_map(&slot->bitmap, map))
828             return 0;
829           last_mode = slot->bitmap.pixel_mode;
830           last_grays = slot->bitmap.num_grays;
831         }
832
833         bmp = slot->bitmap.buffer;
834         for (y = 0; y < slot->bitmap.rows; ++y) {
835           if (last_mode == ft_pixel_mode_grays &&
836               last_grays != 255) {
837             for (x = 0; x < slot->bitmap.width; ++x) 
838               bmp[x] = map[bmp[x]];
839           }
840           i_render_color(render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
841                          slot->bitmap.width, bmp, cl);
842           bmp += slot->bitmap.pitch;
843         }
844       }
845     }
846
847     tx += slot->advance.x / 64;
848     ty -= slot->advance.y / 64;
849   }
850
851   if (aa)
852     i_render_delete(render);
853
854   return 1;
855 }
856
857 /*
858 =item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa)
859
860 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given 
861 I<cheight> and I<cwidth>.
862
863 If align is 0, then the text is rendered with the top-left of the
864 first character at (I<tx>, I<ty>).  If align is non-zero then the text
865 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
866 characters.
867
868 If aa is non-zero then the text is anti-aliased.
869
870 Returns non-zero on success.
871
872 =cut
873 */
874
875 int
876 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, int channel,
877          double cheight, double cwidth, char const *text, size_t len, int align,
878          int aa, int vlayout, int utf8) {
879   i_img_dim bbox[8];
880   i_img *work;
881   i_color cl, cl2;
882   int x, y;
883
884   mm_log((1, "i_ft2_cp(handle %p, im %p, tx %d, ty %d, channel %d, cheight %f, cwidth %f, text %p, len %d, ...)\n", 
885           handle, im, tx, ty, channel, cheight, cwidth, text, len));
886
887   if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
888     i_push_error(0, "face has no vertical metrics");
889     return 0;
890   }
891
892   if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
893     return 0;
894
895   work = i_img_8_new(bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
896   cl.channel[0] = 255;
897   if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth, 
898                   text, len, 1, aa, vlayout, utf8))
899     return 0;
900
901   if (!align) {
902     tx -= bbox[4];
903     ty += bbox[5];
904   }
905   
906   /* render to the specified channel */
907   /* this will be sped up ... */
908   for (y = 0; y < work->ysize; ++y) {
909     for (x = 0; x < work->xsize; ++x) {
910       i_gpix(work, x, y, &cl);
911       i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
912       cl2.channel[channel] = cl.channel[0];
913       i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
914     }
915   }
916   i_img_destroy(work);
917   return 1;
918 }
919
920 /*
921 =item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
922
923 Check if the given characters are defined by the font.
924
925 Returns the number of characters that were checked.
926
927 =cut
928 */
929 size_t
930 i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len, 
931                     int utf8, char *out) {
932   int count = 0;
933   mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
934           handle, text, len, utf8));
935
936   while (len) {
937     unsigned long c;
938     int index;
939     if (utf8) {
940       c = i_utf8_advance(&text, &len);
941       if (c == ~0UL) {
942         i_push_error(0, "invalid UTF8 character");
943         return 0;
944       }
945     }
946     else {
947       c = (unsigned char)*text++;
948       --len;
949     }
950     
951     index = FT_Get_Char_Index(handle->face, c);
952     *out++ = index != 0;
953     ++count;
954   }
955
956   return count;
957 }
958
959 /* uses a method described in fterrors.h to build an error translation
960    function
961 */
962 #undef __FTERRORS_H__
963 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
964 #define FT_ERROR_START_LIST 
965 #define FT_ERROR_END_LIST 
966
967 /*
968 =back
969
970 =head2 Internal Functions
971
972 These functions are used in the implementation of freetyp2.c and should not
973 (usually cannot) be called from outside it.
974
975 =over
976
977 =item ft2_push_message(int code)
978
979 Pushes an error message corresponding to code onto the error stack.
980
981 =cut
982 */
983
984 #define UNKNOWN_ERROR_FORMAT "Unknown Freetype2 error code 0x%04X"
985
986 static void
987 ft2_push_message(int code) {
988   char unknown[40];
989
990   switch (code) {
991 #include FT_ERRORS_H
992   }
993
994 #ifdef IMAGER_SNPRINTF
995   snprintf(unknown, sizeof(unknown), UNKNOWN_ERROR_FORMAT, code);
996 #else
997   sprintf(unknown, UNKNOWN_ERROR_FORMAT, code);
998 #endif
999   i_push_error(code, unknown);
1000 }
1001
1002 /*
1003 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
1004
1005 Creates a map to convert grey levels from the glyphs bitmap into
1006 values scaled 0..255.
1007
1008 =cut
1009 */
1010 static int
1011 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
1012   int scale;
1013   int i;
1014
1015   switch (bitmap->pixel_mode) {
1016   case ft_pixel_mode_grays:
1017     scale = bitmap->num_grays;
1018     break;
1019     
1020   default:
1021     i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
1022     return 0;
1023   }
1024
1025   /* build the table */
1026   for (i = 0; i < scale; ++i)
1027     map[i] = i * 255 / (bitmap->num_grays - 1);
1028
1029   return 1;
1030 }
1031
1032 /* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false 
1033    positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
1034 #ifndef FREETYPE_PATCH
1035 #define FREETYPE_PATCH 4
1036 #endif
1037
1038 /* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
1039 #define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
1040 /* #define IM_HAS_FACE_NAME 0 */ 
1041
1042 /*
1043 =item i_ft2_face_name(handle, name_buf, name_buf_size)
1044
1045 Fills the given buffer with the Postscript Face name of the font,
1046 if there is one.
1047
1048 Returns the number of bytes copied, including the terminating NUL.
1049
1050 =cut
1051 */
1052
1053 size_t
1054 i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1055 #if IM_HAS_FACE_NAME
1056   char const *name = FT_Get_Postscript_Name(handle->face);
1057
1058   i_clear_error();
1059
1060   if (name) {
1061     strncpy(name_buf, name, name_buf_size);
1062     name_buf[name_buf_size-1] = '\0';
1063
1064     return strlen(name) + 1;
1065   }
1066   else {
1067     i_push_error(0, "no face name available");
1068     *name_buf = '\0';
1069
1070     return 0;
1071   }
1072 #else
1073   i_clear_error();
1074   i_push_error(0, "Freetype 2.0.6 or later required");
1075   *name_buf = '\0';
1076
1077   return 0;
1078 #endif
1079 }
1080
1081 int
1082 i_ft2_can_face_name(void) {
1083   return IM_HAS_FACE_NAME;
1084 }
1085
1086 /* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
1087 /* well, I assume FREETYPE_MAJOR is 2, since we're here */
1088 #if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
1089 #define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
1090 #endif
1091
1092 int
1093 i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf, 
1094                  size_t name_buf_size, int reliable_only) {
1095 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1096   i_clear_error();
1097   *name_buf = '\0';
1098   i_push_error(0, "FT2 configured without glyph name support");
1099
1100   return 0;
1101 #else
1102   FT_UInt index;
1103
1104   i_clear_error();
1105
1106   if (!FT_HAS_GLYPH_NAMES(handle->face)) {
1107     i_push_error(0, "no glyph names in font");
1108     *name_buf = '\0';
1109     return 0;
1110   }
1111   if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
1112     i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
1113     *name_buf = '\0';
1114     return 0;
1115   }
1116
1117   index = FT_Get_Char_Index(handle->face, ch);
1118   
1119   if (index) {
1120     FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf, 
1121                                        name_buf_size);
1122     if (error) {
1123       ft2_push_message(error);
1124       *name_buf = '\0';
1125       return 0;
1126     }
1127     if (*name_buf) {
1128       return strlen(name_buf) + 1;
1129     }
1130     else {
1131       return 0;
1132     }
1133   }
1134   else {
1135     i_push_error(0, "no glyph for that character");
1136     *name_buf = 0;
1137     return 0;
1138   }
1139 #endif
1140 }
1141
1142 int
1143 i_ft2_can_do_glyph_names(void) {
1144 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1145   return 0;
1146 #else
1147   return 1;
1148 #endif
1149 }
1150
1151 int 
1152 i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
1153 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1154   return 0;
1155 #else
1156   return FT_Has_PS_Glyph_Names(handle->face);
1157 #endif
1158 }
1159
1160 int
1161 i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
1162   i_clear_error();
1163 #ifdef IM_FT2_MM
1164   return handle->has_mm;
1165 #else
1166   return 0;
1167 #endif
1168 }
1169
1170 int
1171 i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
1172 #ifdef IM_FT2_MM
1173   int i;
1174   FT_Multi_Master *mms = &handle->mm;
1175
1176   i_clear_error();
1177   if (!handle->has_mm) {
1178     i_push_error(0, "Font has no multiple masters");
1179     return 0;
1180   }
1181   mm->num_axis = mms->num_axis;
1182   mm->num_designs = mms->num_designs;
1183   for (i = 0; i < mms->num_axis; ++i) {
1184     mm->axis[i].name = mms->axis[i].name;
1185     mm->axis[i].minimum = mms->axis[i].minimum;
1186     mm->axis[i].maximum = mms->axis[i].maximum;
1187   }
1188
1189   return 1;
1190 #else
1191   i_clear_error();
1192   i_push_error(0, "Multiple master functions unavailable");
1193   return 0;
1194 #endif
1195 }
1196
1197 int
1198 i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords) {
1199 #ifdef IM_FT2_MM
1200   int i;
1201   FT_Long ftcoords[T1_MAX_MM_AXIS];
1202   FT_Error error;
1203
1204   i_clear_error();
1205   if (!handle->has_mm) {
1206     i_push_error(0, "Font has no multiple masters");
1207     return 0;
1208   }
1209   if (coord_count != handle->mm.num_axis) {
1210     i_push_error(0, "Number of MM coords doesn't match MM axis count");
1211     return 0;
1212   }
1213   for (i = 0; i < coord_count; ++i)
1214     ftcoords[i] = coords[i];
1215
1216   error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
1217   if (error) {
1218     ft2_push_message(error);
1219     return 0;
1220   }
1221   
1222   return 1;
1223 #else 
1224   i_clear_error();
1225   i_push_error(0, "Multiple master functions unavailable");
1226
1227   return 0;
1228 #endif
1229 }
1230
1231 static i_img_dim
1232 i_min(i_img_dim a, i_img_dim b) {
1233   return a < b ? a : b;
1234 }
1235
1236 static i_img_dim
1237 i_max(i_img_dim a, i_img_dim b) {
1238   return a > b ? a : b;
1239 }
1240
1241 /*
1242 =back
1243
1244 =head1 AUTHOR
1245
1246 Tony Cook <tony@develop-help.com>, with a fair amount of help from
1247 reading the code in font.c.
1248
1249 =head1 SEE ALSO
1250
1251 font.c, Imager::Font(3), Imager(3)
1252
1253 http://www.freetype.org/
1254
1255 =cut
1256 */
1257