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