]> git.imager.perl.org - imager.git/blob - FT2/freetyp2.c
fix some internal FT2 documentation
[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, vlayout, utf8)) { 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 %X platform %u encoding %u\n",
243             (unsigned)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 %X\n", (unsigned)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 %u, bbox %p)\n",
428           handle, cheight, cwidth, text, (unsigned)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%02lx (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=%" i_DF " glob_desc=%" i_DF " pos_wid=%" i_DF
503           " glob_asc=%" i_DF " desc=%" i_DF " asc=%" i_DF " adv_width=%" i_DF
504           " rightb=%" i_DF "\n",
505           bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6],
506           bbox[7]));
507
508   return BBOX_RIGHT_BEARING + 1;
509 }
510
511 /*
512 =item transform_box(FT2_FontHandle *handle, int bbox[4])
513
514 bbox contains coorinates of a the top-left and bottom-right of a bounding 
515 box relative to a point.
516
517 This is then transformed and the values in bbox[4] are the top-left
518 and bottom-right of the new bounding box.
519
520 This is meant to provide the bounding box of a transformed character
521 box.  The problem is that if the character was round and is rotated,
522 the real bounding box isn't going to be much different from the
523 original, but this function will return a _bigger_ bounding box.  I
524 suppose I could work my way through the glyph outline, but that's
525 too much hard work.
526
527 =cut
528 */
529 void ft2_transform_box(FT2_Fonthandle *handle, i_img_dim bbox[4]) {
530   double work[8];
531   double *matrix = handle->matrix;
532   
533   work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
534   work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
535   work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
536   work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
537   work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
538   work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
539   work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
540   work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
541
542   bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
543   bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
544   bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
545   bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
546 }
547
548 /*
549 =item expand_bounds(int bbox[4], int bbox2[4]) 
550
551 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
552 bounding box in bbox[] that encloses both.
553
554 =cut
555 */
556 static void expand_bounds(i_img_dim bbox[4], i_img_dim bbox2[4]) {
557   bbox[0] = i_min(bbox[0], bbox2[0]);
558   bbox[1] = i_min(bbox[1], bbox2[1]);
559   bbox[2] = i_max(bbox[2], bbox2[2]);
560   bbox[3] = i_max(bbox[3], bbox2[3]);
561 }
562
563 /*
564 =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)
565
566 Retrieves bounding box information for the font at the given 
567 character width and height.
568
569 This version finds the rectangular bounding box of the glyphs, with
570 the text as transformed by the transformation matrix.  As with
571 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
572 the topline to the top-left of the bounding box.  Unlike i_ft2_bbox()
573 this could be near the bottom left corner of the box.
574
575 (bbox[4], bbox[5]) is the offset to the start of the baseline.
576 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
577 end of the baseline.
578
579 Returns non-zero on success.
580
581 =cut
582 */
583 int
584 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, 
585            char const *text, size_t len, int vlayout, int utf8, i_img_dim *bbox) {
586   FT_Error error;
587   i_img_dim width;
588   int index;
589   int first;
590   i_img_dim ascent = 0, descent = 0;
591   int glyph_ascent, glyph_descent;
592   FT_Glyph_Metrics *gm;
593   i_img_dim work[4];
594   i_img_dim bounds[4] = { 0 };
595   double x = 0, y = 0;
596   int i;
597   FT_GlyphSlot slot;
598   int loadFlags = FT_LOAD_DEFAULT;
599
600   if (vlayout)
601     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
602   if (!handle->hint)
603     loadFlags |= FT_LOAD_NO_HINTING;
604
605   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
606                            handle->xdpi, handle->ydpi);
607   if (error) {
608     ft2_push_message(error);
609     i_push_error(0, "setting size");
610   }
611
612   first = 1;
613   width = 0;
614   while (len) {
615     unsigned long c;
616     if (utf8) {
617       c = i_utf8_advance(&text, &len);
618       if (c == ~0UL) {
619         i_push_error(0, "invalid UTF8 character");
620         return 0;
621       }
622     }
623     else {
624       c = (unsigned char)*text++;
625       --len;
626     }
627
628     index = FT_Get_Char_Index(handle->face, c);
629     error = FT_Load_Glyph(handle->face, index, loadFlags);
630     if (error) {
631       ft2_push_message(error);
632       i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
633                     c, index);
634       return 0;
635     }
636     slot = handle->face->glyph; 
637     gm = &slot->metrics;
638
639     /* these probably don't mean much for vertical layouts */
640     glyph_ascent = gm->horiBearingY / 64;
641     glyph_descent = glyph_ascent - gm->height/64;
642     if (vlayout) {
643       work[0] = gm->vertBearingX;
644       work[1] = gm->vertBearingY;
645     }
646     else {
647       work[0] = gm->horiBearingX;
648       work[1] = gm->horiBearingY;
649     }
650     work[2] = gm->width  + work[0];
651     work[3] = work[1] - gm->height;
652     if (first) {
653       bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
654       bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
655       bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
656       bbox[5] /= 64;
657     }
658     ft2_transform_box(handle, work);
659     for (i = 0; i < 4; ++i)
660       work[i] /= 64;
661     work[0] += x;
662     work[1] += y;
663     work[2] += x;
664     work[3] += y;
665     if (first) {
666       for (i = 0; i < 4; ++i)
667         bounds[i] = work[i];
668       ascent = glyph_ascent;
669       descent = glyph_descent;
670       first = 0;
671     }
672     else {
673       expand_bounds(bounds, work);
674     }
675     x += slot->advance.x / 64;
676     y += slot->advance.y / 64;
677
678     if (glyph_ascent > ascent)
679       ascent = glyph_ascent;
680     if (glyph_descent > descent)
681       descent = glyph_descent;
682
683     if (len == 0) {
684       /* last character 
685        handle the case where the right the of the character overlaps the 
686        right*/
687       /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
688       if (rightb < 0)
689       width -= rightb / 64;*/
690     }
691   }
692
693   /* at this point bounds contains the bounds relative to the CP,
694      and x, y hold the final position relative to the CP */
695   /*bounds[0] -= x;
696   bounds[1] -= y;
697   bounds[2] -= x;
698   bounds[3] -= y;*/
699
700   bbox[0] = bounds[0];
701   bbox[1] = -bounds[3];
702   bbox[2] = bounds[2];
703   bbox[3] = -bounds[1];
704   bbox[6] = x;
705   bbox[7] = -y;
706
707   return 1;
708 }
709
710 static int
711 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
712
713 /*
714 =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)
715
716 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given 
717 I<cheight> and I<cwidth>.
718
719 If align is 0, then the text is rendered with the top-left of the
720 first character at (I<tx>, I<ty>).  If align is non-zero then the text
721 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
722 characters.
723
724 If aa is non-zero then the text is anti-aliased.
725
726 Returns non-zero on success.
727
728 =cut
729 */
730 int
731 i_ft2_text(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, const i_color *cl,
732            double cheight, double cwidth, char const *text, size_t len,
733            int align, int aa, int vlayout, int utf8) {
734   FT_Error error;
735   int index;
736   FT_Glyph_Metrics *gm;
737   i_img_dim bbox[BOUNDING_BOX_COUNT];
738   FT_GlyphSlot slot;
739   int x, y;
740   unsigned char *bmp;
741   unsigned char map[256];
742   char last_mode = ft_pixel_mode_none; 
743   int last_grays = -1;
744   int loadFlags = FT_LOAD_DEFAULT;
745   i_render *render = NULL;
746
747   mm_log((1, "i_ft2_text(handle %p, im %p, (tx,ty) (" i_DFp "), cl %p, cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n",
748           handle, im, i_DFcp(tx, ty), cl, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8));
749
750   if (vlayout) {
751     if (!FT_HAS_VERTICAL(handle->face)) {
752       i_push_error(0, "face has no vertical metrics");
753       return 0;
754     }
755     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
756   }
757   if (!handle->hint)
758     loadFlags |= FT_LOAD_NO_HINTING;
759
760   /* set the base-line based on the string ascent */
761   if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
762     return 0;
763
764   if (aa)
765     render = i_render_new(im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
766
767   if (!align) {
768     /* this may need adjustment */
769     tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
770     ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
771   }
772   while (len) {
773     unsigned long c;
774     if (utf8) {
775       c = i_utf8_advance(&text, &len);
776       if (c == ~0UL) {
777         i_push_error(0, "invalid UTF8 character");
778         return 0;
779       }
780     }
781     else {
782       c = (unsigned char)*text++;
783       --len;
784     }
785     
786     index = FT_Get_Char_Index(handle->face, c);
787     error = FT_Load_Glyph(handle->face, index, loadFlags);
788     if (error) {
789       ft2_push_message(error);
790       i_push_errorf(0, "loading glyph for character \\x%02lx (glyph 0x%04X)",
791                     c, index);
792       if (render)
793         i_render_delete(render);
794       return 0;
795     }
796     slot = handle->face->glyph;
797     gm = &slot->metrics;
798
799     if (gm->width) {
800       error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
801       if (error) {
802         ft2_push_message(error);
803         i_push_errorf(0, "rendering glyph 0x%04lX (character \\x%02X)", c, index);
804       if (render)
805         i_render_delete(render);
806         return 0;
807       }
808       if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
809         bmp = slot->bitmap.buffer;
810         for (y = 0; y < slot->bitmap.rows; ++y) {
811           int pos = 0;
812           int bit = 0x80;
813           for (x = 0; x < slot->bitmap.width; ++x) {
814             if (bmp[pos] & bit)
815               i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
816             
817             bit >>= 1;
818             if (bit == 0) {
819               bit = 0x80;
820               ++pos;
821             }
822           }
823           bmp += slot->bitmap.pitch;
824         }
825       }
826       else {
827         /* grey scale or something we can treat as greyscale */
828         /* we create a map to convert from the bitmap values to 0-255 */
829         if (last_mode != slot->bitmap.pixel_mode 
830             || last_grays != slot->bitmap.num_grays) {
831           if (!make_bmp_map(&slot->bitmap, map))
832             return 0;
833           last_mode = slot->bitmap.pixel_mode;
834           last_grays = slot->bitmap.num_grays;
835         }
836
837         bmp = slot->bitmap.buffer;
838         for (y = 0; y < slot->bitmap.rows; ++y) {
839           if (last_mode == ft_pixel_mode_grays &&
840               last_grays != 255) {
841             for (x = 0; x < slot->bitmap.width; ++x) 
842               bmp[x] = map[bmp[x]];
843           }
844           i_render_color(render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
845                          slot->bitmap.width, bmp, cl);
846           bmp += slot->bitmap.pitch;
847         }
848       }
849     }
850
851     tx += slot->advance.x / 64;
852     ty -= slot->advance.y / 64;
853   }
854
855   if (render)
856     i_render_delete(render);
857
858   return 1;
859 }
860
861 /*
862 =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, int vlayout, int utf8)
863
864 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given 
865 I<cheight> and I<cwidth>.
866
867 If align is 0, then the text is rendered with the top-left of the
868 first character at (I<tx>, I<ty>).  If align is non-zero then the text
869 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
870 characters.
871
872 If C<utf8> is non-zero the text is treated as UTF-8 encoded
873
874 If C<aa> is non-zero then the text is drawn anti-aliased.
875
876 Returns non-zero on success.
877
878 =cut
879 */
880
881 int
882 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, i_img_dim tx, i_img_dim ty, int channel,
883          double cheight, double cwidth, char const *text, size_t len, int align,
884          int aa, int vlayout, int utf8) {
885   i_img_dim bbox[8];
886   i_img *work;
887   i_color cl, cl2;
888   int x, y;
889
890   mm_log((1, "i_ft2_cp(handle %p, im %p, (tx, ty) (" i_DFp "), channel %d, cheight %f, cwidth %f, text %p, len %u, align %d, aa %d, vlayout %d, utf8 %d)\n", 
891           handle, im, i_DFcp(tx, ty), channel, cheight, cwidth, text, (unsigned)len, align, aa, vlayout, utf8));
892
893   if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
894     i_push_error(0, "face has no vertical metrics");
895     return 0;
896   }
897
898   if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
899     return 0;
900
901   work = i_img_8_new(bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
902   cl.channel[0] = 255;
903   if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth, 
904                   text, len, 1, aa, vlayout, utf8))
905     return 0;
906
907   if (!align) {
908     tx -= bbox[4];
909     ty += bbox[5];
910   }
911   
912   /* render to the specified channel */
913   /* this will be sped up ... */
914   for (y = 0; y < work->ysize; ++y) {
915     for (x = 0; x < work->xsize; ++x) {
916       i_gpix(work, x, y, &cl);
917       i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
918       cl2.channel[channel] = cl.channel[0];
919       i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
920     }
921   }
922   i_img_destroy(work);
923   return 1;
924 }
925
926 /*
927 =item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
928
929 Check if the given characters are defined by the font.
930
931 Returns the number of characters that were checked.
932
933 =cut
934 */
935 size_t
936 i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len, 
937                     int utf8, char *out) {
938   int count = 0;
939   mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %u, utf8 %d)\n", 
940           handle, text, (unsigned)len, utf8));
941
942   while (len) {
943     unsigned long c;
944     int index;
945     if (utf8) {
946       c = i_utf8_advance(&text, &len);
947       if (c == ~0UL) {
948         i_push_error(0, "invalid UTF8 character");
949         return 0;
950       }
951     }
952     else {
953       c = (unsigned char)*text++;
954       --len;
955     }
956     
957     index = FT_Get_Char_Index(handle->face, c);
958     *out++ = index != 0;
959     ++count;
960   }
961
962   return count;
963 }
964
965 /* uses a method described in fterrors.h to build an error translation
966    function
967 */
968 #undef __FTERRORS_H__
969 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
970 #define FT_ERROR_START_LIST 
971 #define FT_ERROR_END_LIST 
972
973 /*
974 =back
975
976 =head2 Internal Functions
977
978 These functions are used in the implementation of freetyp2.c and should not
979 (usually cannot) be called from outside it.
980
981 =over
982
983 =item ft2_push_message(int code)
984
985 Pushes an error message corresponding to code onto the error stack.
986
987 =cut
988 */
989
990 #define UNKNOWN_ERROR_FORMAT "Unknown Freetype2 error code 0x%04X"
991
992 static void
993 ft2_push_message(int code) {
994   char unknown[40];
995
996   switch (code) {
997 #include FT_ERRORS_H
998   }
999
1000 #ifdef IMAGER_SNPRINTF
1001   snprintf(unknown, sizeof(unknown), UNKNOWN_ERROR_FORMAT, code);
1002 #else
1003   sprintf(unknown, UNKNOWN_ERROR_FORMAT, code);
1004 #endif
1005   i_push_error(code, unknown);
1006 }
1007
1008 /*
1009 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
1010
1011 Creates a map to convert grey levels from the glyphs bitmap into
1012 values scaled 0..255.
1013
1014 =cut
1015 */
1016 static int
1017 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
1018   int scale;
1019   int i;
1020
1021   switch (bitmap->pixel_mode) {
1022   case ft_pixel_mode_grays:
1023     scale = bitmap->num_grays;
1024     break;
1025     
1026   default:
1027     i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
1028     return 0;
1029   }
1030
1031   /* build the table */
1032   for (i = 0; i < scale; ++i)
1033     map[i] = i * 255 / (bitmap->num_grays - 1);
1034
1035   return 1;
1036 }
1037
1038 /* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false 
1039    positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
1040 #ifndef FREETYPE_PATCH
1041 #define FREETYPE_PATCH 4
1042 #endif
1043
1044 /* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
1045 #define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
1046 /* #define IM_HAS_FACE_NAME 0 */ 
1047
1048 /*
1049 =item i_ft2_face_name(handle, name_buf, name_buf_size)
1050
1051 Fills the given buffer with the Postscript Face name of the font,
1052 if there is one.
1053
1054 Returns the number of bytes copied, including the terminating NUL.
1055
1056 =cut
1057 */
1058
1059 size_t
1060 i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1061 #if IM_HAS_FACE_NAME
1062   char const *name = FT_Get_Postscript_Name(handle->face);
1063
1064   i_clear_error();
1065
1066   if (name) {
1067     strncpy(name_buf, name, name_buf_size);
1068     name_buf[name_buf_size-1] = '\0';
1069
1070     return strlen(name) + 1;
1071   }
1072   else {
1073     i_push_error(0, "no face name available");
1074     *name_buf = '\0';
1075
1076     return 0;
1077   }
1078 #else
1079   i_clear_error();
1080   i_push_error(0, "Freetype 2.0.6 or later required");
1081   *name_buf = '\0';
1082
1083   return 0;
1084 #endif
1085 }
1086
1087 int
1088 i_ft2_can_face_name(void) {
1089   return IM_HAS_FACE_NAME;
1090 }
1091
1092 /* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
1093 /* well, I assume FREETYPE_MAJOR is 2, since we're here */
1094 #if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
1095 #define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
1096 #endif
1097
1098 int
1099 i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf, 
1100                  size_t name_buf_size, int reliable_only) {
1101 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1102   i_clear_error();
1103   *name_buf = '\0';
1104   i_push_error(0, "FT2 configured without glyph name support");
1105
1106   return 0;
1107 #else
1108   FT_UInt index;
1109
1110   i_clear_error();
1111
1112   if (!FT_HAS_GLYPH_NAMES(handle->face)) {
1113     i_push_error(0, "no glyph names in font");
1114     *name_buf = '\0';
1115     return 0;
1116   }
1117   if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
1118     i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
1119     *name_buf = '\0';
1120     return 0;
1121   }
1122
1123   index = FT_Get_Char_Index(handle->face, ch);
1124   
1125   if (index) {
1126     FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf, 
1127                                        name_buf_size);
1128     if (error) {
1129       ft2_push_message(error);
1130       *name_buf = '\0';
1131       return 0;
1132     }
1133     if (*name_buf) {
1134       return strlen(name_buf) + 1;
1135     }
1136     else {
1137       return 0;
1138     }
1139   }
1140   else {
1141     i_push_error(0, "no glyph for that character");
1142     *name_buf = 0;
1143     return 0;
1144   }
1145 #endif
1146 }
1147
1148 int
1149 i_ft2_can_do_glyph_names(void) {
1150 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1151   return 0;
1152 #else
1153   return 1;
1154 #endif
1155 }
1156
1157 int 
1158 i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
1159 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
1160   return 0;
1161 #else
1162   return FT_Has_PS_Glyph_Names(handle->face);
1163 #endif
1164 }
1165
1166 int
1167 i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
1168   i_clear_error();
1169 #ifdef IM_FT2_MM
1170   return handle->has_mm;
1171 #else
1172   return 0;
1173 #endif
1174 }
1175
1176 int
1177 i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
1178 #ifdef IM_FT2_MM
1179   int i;
1180   FT_Multi_Master *mms = &handle->mm;
1181
1182   i_clear_error();
1183   if (!handle->has_mm) {
1184     i_push_error(0, "Font has no multiple masters");
1185     return 0;
1186   }
1187   mm->num_axis = mms->num_axis;
1188   mm->num_designs = mms->num_designs;
1189   for (i = 0; i < mms->num_axis; ++i) {
1190     mm->axis[i].name = mms->axis[i].name;
1191     mm->axis[i].minimum = mms->axis[i].minimum;
1192     mm->axis[i].maximum = mms->axis[i].maximum;
1193   }
1194
1195   return 1;
1196 #else
1197   i_clear_error();
1198   i_push_error(0, "Multiple master functions unavailable");
1199   return 0;
1200 #endif
1201 }
1202
1203 int
1204 i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords) {
1205 #ifdef IM_FT2_MM
1206   int i;
1207   FT_Long ftcoords[T1_MAX_MM_AXIS];
1208   FT_Error error;
1209
1210   i_clear_error();
1211   if (!handle->has_mm) {
1212     i_push_error(0, "Font has no multiple masters");
1213     return 0;
1214   }
1215   if (coord_count != handle->mm.num_axis) {
1216     i_push_error(0, "Number of MM coords doesn't match MM axis count");
1217     return 0;
1218   }
1219   for (i = 0; i < coord_count; ++i)
1220     ftcoords[i] = coords[i];
1221
1222   error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
1223   if (error) {
1224     ft2_push_message(error);
1225     return 0;
1226   }
1227   
1228   return 1;
1229 #else 
1230   i_clear_error();
1231   i_push_error(0, "Multiple master functions unavailable");
1232
1233   return 0;
1234 #endif
1235 }
1236
1237 static i_img_dim
1238 i_min(i_img_dim a, i_img_dim b) {
1239   return a < b ? a : b;
1240 }
1241
1242 static i_img_dim
1243 i_max(i_img_dim a, i_img_dim b) {
1244   return a > b ? a : b;
1245 }
1246
1247 /*
1248 =back
1249
1250 =head1 AUTHOR
1251
1252 Tony Cook <tony@develop-help.com>, with a fair amount of help from
1253 reading the code in font.c.
1254
1255 =head1 SEE ALSO
1256
1257 font.c, Imager::Font(3), Imager(3)
1258
1259 http://www.freetype.org/
1260
1261 =cut
1262 */
1263