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