]> git.imager.perl.org - imager.git/blob - freetyp2.c
- i_box_cfill() went into an infinite loop on fountain fills
[imager.git] / 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   int bbox[6];
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 "image.h"
38 #include <stdio.h>
39 #include <ft2build.h>
40 #include FT_FREETYPE_H
41
42 static void ft2_push_message(int code);
43 static unsigned long utf8_advance(char **p, int *len);
44
45 static FT_Library library;
46
47 /*
48 =item i_ft2_init(void)
49
50 Initializes the Freetype 2 library.
51
52 Returns true on success, false on failure.
53
54 =cut
55 */
56 int
57 i_ft2_init(void) {
58   FT_Error error;
59
60   i_clear_error();
61   error = FT_Init_FreeType(&library);
62   if (error) {
63     ft2_push_message(error);
64     i_push_error(0, "Initializing Freetype2");
65     return 0;
66   }
67   return 1;
68 }
69
70 struct FT2_Fonthandle {
71   FT_Face face;
72   int xdpi, ydpi;
73   int hint;
74   FT_Encoding encoding;
75
76   /* used to adjust so we can align the draw point to the top-left */
77   double matrix[6];
78 };
79
80 /* the following is used to select a "best" encoding */
81 static struct enc_score {
82   FT_Encoding encoding;
83   int score;
84 } enc_scores[] =
85 {
86   /* the selections here are fairly arbitrary
87      ideally we need to give the user a list of encodings available
88      and a mechanism to choose one */
89   { ft_encoding_unicode,        10 },
90   { ft_encoding_sjis,            8 },
91   { ft_encoding_gb2312,          8 },
92   { ft_encoding_big5,            8 },
93   { ft_encoding_wansung,         8 },
94   { ft_encoding_johab,           8 },  
95   { ft_encoding_latin_2,         6 },
96   { ft_encoding_apple_roman,     6 },
97   { ft_encoding_adobe_standard,  6 },
98   { ft_encoding_adobe_expert,    6 },
99 };
100
101 /*
102 =item i_ft2_new(char *name, int index)
103
104 Creates a new font object, from the file given by I<name>.  I<index>
105 is the index of the font in a file with multiple fonts, where 0 is the
106 first font.
107
108 Return NULL on failure.
109
110 =cut
111 */
112
113 FT2_Fonthandle *
114 i_ft2_new(char *name, int index) {
115   FT_Error error;
116   FT2_Fonthandle *result;
117   FT_Face face;
118   double matrix[6] = { 1, 0, 0,
119                        0, 1, 0 };
120   int i, j;
121   FT_Encoding encoding;
122   int score;
123
124   mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
125
126   i_clear_error();
127   error = FT_New_Face(library, name, index, &face);
128   if (error) {
129     ft2_push_message(error);
130     i_push_error(error, "Opening face");
131     return NULL;
132   }
133
134   encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
135   score = 0;
136   for (i = 0; i < face->num_charmaps; ++i) {
137     FT_Encoding enc_entry = face->charmaps[i]->encoding;
138     mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n",
139             enc_entry, face->charmaps[i]->platform_id,
140             face->charmaps[i]->encoding_id));
141     for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
142       if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
143         encoding = enc_entry;
144         score = enc_scores[j].score;
145         break;
146       }
147     }
148   }
149   FT_Select_Charmap(face, encoding);
150   mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding));
151
152   result = mymalloc(sizeof(FT2_Fonthandle));
153   result->face = face;
154   result->xdpi = result->ydpi = 72;
155   result->encoding = encoding;
156
157   /* by default we disable hinting on a call to i_ft2_settransform()
158      if we don't do this, then the hinting can the untransformed text
159      to be a different size to the transformed text.
160      Obviously we have it initially enabled.
161   */
162   result->hint = 1; 
163
164   /* I originally forgot this:   :/ */
165   /*i_ft2_settransform(result, matrix); */
166   result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
167   result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
168
169   return result;
170 }
171
172 /*
173 =item i_ft2_destroy(FT2_Fonthandle *handle)
174
175 Destroys a font object, which must have been the return value of
176 i_ft2_new().
177
178 =cut
179 */
180 void
181 i_ft2_destroy(FT2_Fonthandle *handle) {
182   FT_Done_Face(handle->face);
183   myfree(handle);
184 }
185
186 /*
187 =item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
188
189 Sets the resolution in dots per inch at which point sizes scaled, by
190 default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
191
192 Both xdpi and ydpi should be positive.
193
194 Return true on success.
195
196 =cut
197 */
198 int
199 i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
200   i_clear_error();
201   if (xdpi > 0 && ydpi > 0) {
202     handle->xdpi = xdpi;
203     handle->ydpi = ydpi;
204     return 0;
205   }
206   else {
207     i_push_error(0, "resolutions must be positive");
208     return 0;
209   }
210 }
211
212 /*
213 =item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
214
215 Retrieves the current horizontal and vertical resolutions at which
216 point sizes are scaled.
217
218 =cut
219 */
220 int
221 i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
222   *xdpi = handle->xdpi;
223   *ydpi = handle->ydpi;
224
225   return 1;
226 }
227
228 /*
229 =item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
230
231 Sets a transormation matrix for output.
232
233 This should be a 2 x 3 matrix like:
234
235  matrix[0]   matrix[1]   matrix[2]
236  matrix[3]   matrix[4]   matrix[5]
237
238 =cut
239 */
240 int
241 i_ft2_settransform(FT2_Fonthandle *handle, double *matrix) {
242   FT_Matrix m;
243   FT_Vector v;
244   int i;
245
246   m.xx = matrix[0] * 65536;
247   m.xy = matrix[1] * 65536;
248   v.x  = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
249   m.yx = matrix[3] * 65536;
250   m.yy = matrix[4] * 65536;
251   v.y  = matrix[5]; /* see just above */
252
253   FT_Set_Transform(handle->face, &m, &v);
254
255   for (i = 0; i < 6; ++i)
256     handle->matrix[i] = matrix[i];
257   handle->hint = 0;
258
259   return 1;
260 }
261
262 /*
263 =item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
264
265 If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
266
267 i_ft2_settransform() disables hinting to prevent distortions in
268 gradual text transformations.
269
270 =cut
271 */
272 int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
273   handle->hint = hinting;
274   return 1;
275 }
276
277 /*
278 =item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int *bbox)
279
280 Retrieves bounding box information for the font at the given 
281 character width and height.  This ignores the transformation matrix.
282
283 Returns non-zero on success.
284
285 =cut
286 */
287 int
288 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, 
289            char *text, int len, int *bbox, int utf8) {
290   FT_Error error;
291   int width;
292   int index;
293   int first;
294   int ascent = 0, descent = 0;
295   int glyph_ascent, glyph_descent;
296   FT_Glyph_Metrics *gm;
297   int start = 0;
298
299   mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
300           handle, cheight, cwidth, text, len, bbox));
301
302   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
303                            handle->xdpi, handle->ydpi);
304   if (error) {
305     ft2_push_message(error);
306     i_push_error(0, "setting size");
307   }
308
309   first = 1;
310   width = 0;
311   while (len) {
312     unsigned long c;
313     if (utf8) {
314       c = utf8_advance(&text, &len);
315       if (c == ~0UL) {
316         i_push_error(0, "invalid UTF8 character");
317         return 0;
318       }
319     }
320     else {
321       c = (unsigned char)*text++;
322       --len;
323     }
324
325     index = FT_Get_Char_Index(handle->face, c);
326     error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
327     if (error) {
328       ft2_push_message(error);
329       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
330                     c, index);
331       return 0;
332     }
333     gm = &handle->face->glyph->metrics;
334     glyph_ascent = gm->horiBearingY / 64;
335     glyph_descent = glyph_ascent - gm->height/64;
336     if (first) {
337       start = gm->horiBearingX / 64;
338       /* handles -ve values properly */
339       ascent = glyph_ascent;
340       descent = glyph_descent;
341       first = 0;
342     }
343
344     if (glyph_ascent > ascent)
345       ascent = glyph_ascent;
346     if (glyph_descent < descent)
347       descent = glyph_descent;
348
349     width += gm->horiAdvance / 64;
350
351     if (len == 0) {
352       /* last character 
353        handle the case where the right the of the character overlaps the 
354        right*/
355       int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
356       if (rightb < 0)
357         width -= rightb / 64;
358     }
359   }
360
361   bbox[0] = start;
362   bbox[1] = handle->face->size->metrics.descender / 64;
363   bbox[2] = width;
364   bbox[3] = handle->face->size->metrics.ascender / 64;
365   bbox[4] = descent;
366   bbox[5] = ascent;
367
368   return 1;
369 }
370
371 /*
372 =item transform_box(FT2_FontHandle *handle, int bbox[4])
373
374 bbox contains coorinates of a the top-left and bottom-right of a bounding 
375 box relative to a point.
376
377 This is then transformed and the values in bbox[4] are the top-left
378 and bottom-right of the new bounding box.
379
380 This is meant to provide the bounding box of a transformed character
381 box.  The problem is that if the character was round and is rotated,
382 the real bounding box isn't going to be much different from the
383 original, but this function will return a _bigger_ bounding box.  I
384 suppose I could work my way through the glyph outline, but that's
385 too much hard work.
386
387 =cut
388 */
389 void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
390   double work[8];
391   double *matrix = handle->matrix;
392   int i;
393   
394   work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
395   work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
396   work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
397   work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
398   work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
399   work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
400   work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
401   work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
402
403   bbox[0] = floor(min(min(work[0], work[2]),min(work[4], work[6])));
404   bbox[1] = floor(min(min(work[1], work[3]),min(work[5], work[7])));
405   bbox[2] = ceil(max(max(work[0], work[2]),max(work[4], work[6])));
406   bbox[3] = ceil(max(max(work[1], work[3]),max(work[5], work[7])));
407 }
408
409 /*
410 =item expand_bounds(int bbox[4], int bbox2[4]) 
411
412 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
413 bounding box in bbox[] that encloses both.
414
415 =cut
416 */
417 static void expand_bounds(int bbox[4], int bbox2[4]) {
418   bbox[0] = min(bbox[0], bbox2[0]);
419   bbox[1] = min(bbox[1], bbox2[1]);
420   bbox[2] = max(bbox[2], bbox2[2]);
421   bbox[3] = max(bbox[3], bbox2[3]);
422 }
423
424 /*
425 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
426
427 Retrieves bounding box information for the font at the given 
428 character width and height.
429
430 This version finds the rectangular bounding box of the glyphs, with
431 the text as transformed by the transformation matrix.  As with
432 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
433 the topline to the top-left of the bounding box.  Unlike i_ft2_bbox()
434 this could be near the bottom left corner of the box.
435
436 (bbox[4], bbox[5]) is the offset to the start of the baseline.
437 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
438 end of the baseline.
439
440 Returns non-zero on success.
441
442 =cut
443 */
444 int
445 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, 
446            char *text, int len, int vlayout, int utf8, int *bbox) {
447   FT_Error error;
448   int width;
449   int index;
450   int first;
451   int ascent = 0, descent = 0;
452   int glyph_ascent, glyph_descent;
453   FT_Glyph_Metrics *gm;
454   int start = 0;
455   int work[4];
456   int bounds[4];
457   double x = 0, y = 0;
458   int i;
459   FT_GlyphSlot slot;
460   int advx, advy;
461   int loadFlags = FT_LOAD_DEFAULT;
462
463   if (vlayout)
464     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
465
466   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
467                            handle->xdpi, handle->ydpi);
468   if (error) {
469     ft2_push_message(error);
470     i_push_error(0, "setting size");
471   }
472
473   first = 1;
474   width = 0;
475   while (len) {
476     unsigned long c;
477     if (utf8) {
478       c = utf8_advance(&text, &len);
479       if (c == ~0UL) {
480         i_push_error(0, "invalid UTF8 character");
481         return 0;
482       }
483     }
484     else {
485       c = (unsigned char)*text++;
486       --len;
487     }
488
489     index = FT_Get_Char_Index(handle->face, c);
490     error = FT_Load_Glyph(handle->face, index, loadFlags);
491     if (error) {
492       ft2_push_message(error);
493       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
494                     c, index);
495       return 0;
496     }
497     slot = handle->face->glyph; 
498     gm = &slot->metrics;
499
500     /* these probably don't mean much for vertical layouts */
501     glyph_ascent = gm->horiBearingY / 64;
502     glyph_descent = glyph_ascent - gm->height/64;
503     if (vlayout) {
504       work[0] = gm->vertBearingX;
505       work[1] = gm->vertBearingY;
506     }
507     else {
508       work[0] = gm->horiBearingX;
509       work[1] = gm->horiBearingY;
510     }
511     work[2] = gm->width  + work[0];
512     work[3] = work[1] - gm->height;
513     if (first) {
514       bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
515       bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
516       bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
517       bbox[5] /= 64;
518     }
519     ft2_transform_box(handle, work);
520     for (i = 0; i < 4; ++i)
521       work[i] /= 64;
522     work[0] += x;
523     work[1] += y;
524     work[2] += x;
525     work[3] += y;
526     if (first) {
527       for (i = 0; i < 4; ++i)
528         bounds[i] = work[i];
529       ascent = glyph_ascent;
530       descent = glyph_descent;
531       first = 0;
532     }
533     else {
534       expand_bounds(bounds, work);
535     }
536     x += slot->advance.x / 64;
537     y += slot->advance.y / 64;
538     
539     if (glyph_ascent > ascent)
540       ascent = glyph_ascent;
541     if (glyph_descent > descent)
542       descent = glyph_descent;
543
544     if (len == 0) {
545       /* last character 
546        handle the case where the right the of the character overlaps the 
547        right*/
548       /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
549       if (rightb < 0)
550       width -= rightb / 64;*/
551     }
552   }
553
554   /* at this point bounds contains the bounds relative to the CP,
555      and x, y hold the final position relative to the CP */
556   /*bounds[0] -= x;
557   bounds[1] -= y;
558   bounds[2] -= x;
559   bounds[3] -= y;*/
560
561   bbox[0] = bounds[0];
562   bbox[1] = -bounds[3];
563   bbox[2] = bounds[2];
564   bbox[3] = -bounds[1];
565   bbox[6] = x;
566   bbox[7] = -y;
567
568   return 1;
569 }
570
571
572
573 static int
574 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
575
576 /*
577 =item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, int len, int align, int aa)
578
579 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given 
580 I<cheight> and I<cwidth>.
581
582 If align is 0, then the text is rendered with the top-left of the
583 first character at (I<tx>, I<ty>).  If align is non-zero then the text
584 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
585 characters.
586
587 If aa is non-zero then the text is anti-aliased.
588
589 Returns non-zero on success.
590
591 =cut
592 */
593 int
594 i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
595            double cheight, double cwidth, char *text, int len, int align,
596            int aa, int vlayout, int utf8) {
597   FT_Error error;
598   int index;
599   FT_Glyph_Metrics *gm;
600   int bbox[6];
601   FT_GlyphSlot slot;
602   int x, y;
603   unsigned char *bmp;
604   unsigned char map[256];
605   char last_mode = ft_pixel_mode_none; 
606   int last_grays = -1;
607   int ch;
608   i_color pel;
609   int loadFlags = FT_LOAD_DEFAULT;
610
611   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",
612           handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
613
614   if (vlayout) {
615     if (!FT_HAS_VERTICAL(handle->face)) {
616       i_push_error(0, "face has no vertical metrics");
617       return 0;
618     }
619     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
620   }
621   if (!handle->hint)
622     loadFlags |= FT_LOAD_NO_HINTING;
623
624   /* set the base-line based on the string ascent */
625   if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
626     return 0;
627
628   if (!align) {
629     /* this may need adjustment */
630     tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
631     ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
632   }
633   while (len) {
634     unsigned long c;
635     if (utf8) {
636       c = utf8_advance(&text, &len);
637       if (c == ~0UL) {
638         i_push_error(0, "invalid UTF8 character");
639         return 0;
640       }
641     }
642     else {
643       c = (unsigned char)*text++;
644       --len;
645     }
646     
647     index = FT_Get_Char_Index(handle->face, c);
648     error = FT_Load_Glyph(handle->face, index, loadFlags);
649     if (error) {
650       ft2_push_message(error);
651       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
652                     c, index);
653       return 0;
654     }
655     slot = handle->face->glyph;
656     gm = &slot->metrics;
657
658     error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
659     if (error) {
660       ft2_push_message(error);
661       i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
662       return 0;
663     }
664     if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
665       bmp = slot->bitmap.buffer;
666       for (y = 0; y < slot->bitmap.rows; ++y) {
667         int pos = 0;
668         int bit = 0x80;
669         for (x = 0; x < slot->bitmap.width; ++x) {
670           if (bmp[pos] & bit)
671             i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
672
673           bit >>= 1;
674           if (bit == 0) {
675             bit = 0x80;
676             ++pos;
677           }
678         }
679         bmp += slot->bitmap.pitch;
680       }
681     }
682     else {
683       /* grey scale or something we can treat as greyscale */
684       /* we create a map to convert from the bitmap values to 0-255 */
685       if (last_mode != slot->bitmap.pixel_mode 
686           || last_grays != slot->bitmap.num_grays) {
687         if (!make_bmp_map(&slot->bitmap, map))
688           return 0;
689         last_mode = slot->bitmap.pixel_mode;
690         last_grays = slot->bitmap.num_grays;
691       }
692       
693       bmp = slot->bitmap.buffer;
694       for (y = 0; y < slot->bitmap.rows; ++y) {
695         for (x = 0; x < slot->bitmap.width; ++x) {
696           int value = map[bmp[x]];
697           if (value) {
698             i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
699             for (ch = 0; ch < im->channels; ++ch) {
700               pel.channel[ch] = 
701                 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
702             }
703             i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
704           }
705         }
706         bmp += slot->bitmap.pitch;
707       }
708     }
709
710     tx += slot->advance.x / 64;
711     ty -= slot->advance.y / 64;
712   }
713
714   return 1;
715 }
716
717 /*
718 =item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, int len, int align, int aa)
719
720 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given 
721 I<cheight> and I<cwidth>.
722
723 If align is 0, then the text is rendered with the top-left of the
724 first character at (I<tx>, I<ty>).  If align is non-zero then the text
725 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
726 characters.
727
728 If aa is non-zero then the text is anti-aliased.
729
730 Returns non-zero on success.
731
732 =cut
733 */
734
735 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
736          double cheight, double cwidth, char *text, int len, int align,
737          int aa, int vlayout, int utf8) {
738   int bbox[8];
739   i_img *work;
740   i_color cl, cl2;
741   int x, y;
742
743   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", 
744           handle, im, tx, ty, channel, cheight, cwidth, text, len));
745
746   if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
747     i_push_error(0, "face has no vertical metrics");
748     return 0;
749   }
750
751   if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
752     return 0;
753
754   work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
755   cl.channel[0] = 255;
756   if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth, 
757                   text, len, 1, aa, vlayout, utf8))
758     return 0;
759
760   if (!align) {
761     tx -= bbox[4];
762     ty += bbox[5];
763   }
764   
765   /* render to the specified channel */
766   /* this will be sped up ... */
767   for (y = 0; y < work->ysize; ++y) {
768     for (x = 0; x < work->xsize; ++x) {
769       i_gpix(work, x, y, &cl);
770       i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
771       cl2.channel[channel] = cl.channel[0];
772       i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
773     }
774   }
775   i_img_destroy(work);
776   return 1;
777 }
778
779 /*
780 =item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
781
782 Check if the given characters are defined by the font.
783
784 Returns the number of characters that were checked.
785
786 =cut
787 */
788 int i_ft2_has_chars(FT2_Fonthandle *handle, char *text, int len, int utf8, 
789                        char *out) {
790   int count = 0;
791   mm_log((1, "i_ft2_check_chars(handle %p, text %p, len %d, utf8 %d)\n", 
792           handle, text, len, utf8));
793
794   while (len) {
795     unsigned long c;
796     int index;
797     if (utf8) {
798       c = utf8_advance(&text, &len);
799       if (c == ~0UL) {
800         i_push_error(0, "invalid UTF8 character");
801         return 0;
802       }
803     }
804     else {
805       c = (unsigned char)*text++;
806       --len;
807     }
808     
809     index = FT_Get_Char_Index(handle->face, c);
810     *out++ = index != 0;
811     ++count;
812   }
813
814   return count;
815 }
816
817 /* uses a method described in fterrors.h to build an error translation
818    function
819 */
820 #undef __FT_ERRORS_H__
821 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
822 #define FT_ERROR_START_LIST
823 #define FT_ERROR_END_LIST
824
825 /*
826 =back
827
828 =head2 Internal Functions
829
830 These functions are used in the implementation of freetyp2.c and should not
831 (usually cannot) be called from outside it.
832
833 =over
834
835 =item ft2_push_message(int code)
836
837 Pushes an error message corresponding to code onto the error stack.
838
839 =cut
840 */
841 static void ft2_push_message(int code) {
842   char unknown[40];
843
844   switch (code) {
845 #include FT_ERRORS_H
846   }
847
848   sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
849   i_push_error(code, unknown);
850 }
851
852 /*
853 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
854
855 Creates a map to convert grey levels from the glyphs bitmap into
856 values scaled 0..255.
857
858 =cut
859 */
860 static int
861 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
862   int scale;
863   int i;
864
865   switch (bitmap->pixel_mode) {
866   case ft_pixel_mode_grays:
867     scale = bitmap->num_grays;
868     break;
869     
870   default:
871     i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
872     return 0;
873   }
874
875   /* build the table */
876   for (i = 0; i < scale; ++i)
877     map[i] = i * 255 / (bitmap->num_grays - 1);
878
879   return 1;
880 }
881
882 struct utf8_size {
883   int mask, expect;
884   int size;
885 };
886
887 struct utf8_size utf8_sizes[] =
888 {
889   { 0x80, 0x00, 1 },
890   { 0xE0, 0xC0, 2 },
891   { 0xF0, 0xE0, 3 },
892   { 0xF8, 0xF0, 4 },
893 };
894
895 /*
896 =item utf8_advance(char **p, int *len)
897
898 Retreive a UTF8 character from the stream.
899
900 Modifies *p and *len to indicate the consumed characters.
901
902 This doesn't support the extended UTF8 encoding used by later versions
903 of Perl.
904
905 =cut
906 */
907
908 unsigned long utf8_advance(char **p, int *len) {
909   unsigned char c;
910   int i, ci, clen = 0;
911   unsigned char codes[3];
912   if (*len == 0)
913     return ~0UL;
914   c = *(*p)++; --*len;
915
916   for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) {
917     if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) {
918       clen = utf8_sizes[i].size;
919     }
920   }
921   if (clen == 0 || *len < clen-1) {
922     --*p; ++*len;
923     return ~0UL;
924   }
925
926   /* check that each character is well formed */
927   i = 1;
928   ci = 0;
929   while (i < clen) {
930     if (((*p)[ci] & 0xC0) != 0x80) {
931       --*p; ++*len;
932       return ~0UL;
933     }
934     codes[ci] = (*p)[ci];
935     ++ci; ++i;
936   }
937   *p += clen-1; *len -= clen-1;
938   if (c & 0x80) {
939     if ((c & 0xE0) == 0xC0) {
940       return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
941     }
942     else if ((c & 0xF0) == 0xE0) {
943       return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
944     }
945     else if ((c & 0xF8) == 0xF0) {
946       return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12) 
947               | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
948     }
949     else {
950       *p -= clen; *len += clen;
951       return ~0UL;
952     }
953   }
954   else {
955     return c;
956   }
957 }
958
959 /*
960 =back
961
962 =head1 AUTHOR
963
964 Tony Cook <tony@develop-help.com>, with a fair amount of help from
965 reading the code in font.c.
966
967 =head1 SEE ALSO
968
969 font.c, Imager::Font(3), Imager(3)
970
971 http://www.freetype.org/
972
973 =cut
974 */
975