]> git.imager.perl.org - imager.git/blob - freetyp2.c
Added preliminary support for adding image based fonts.
[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[BOUNDING_BOX_COUNT];
16   if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
17   i_img *im = ...;
18   i_color cl;
19   if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align,
20                   aa)) { error }
21   if (!i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, length,
22                 align, aa)) { error }
23   i_ft2_destroy(font);
24
25 =head1 DESCRIPTION
26
27 Implements Imager font support using the FreeType2 library.
28
29 The FreeType2 library understands several font file types, including
30 Truetype, Type1 and Windows FNT.
31
32 =over 
33
34 =cut
35 */
36
37 #include "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   int loadFlags = FT_LOAD_DEFAULT;
298   int rightb;
299
300   mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
301           handle, cheight, cwidth, text, len, bbox));
302
303   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
304                            handle->xdpi, handle->ydpi);
305   if (error) {
306     ft2_push_message(error);
307     i_push_error(0, "setting size");
308   }
309
310   if (!handle->hint)
311     loadFlags |= FT_LOAD_NO_HINTING;
312
313   first = 1;
314   width = 0;
315   while (len) {
316     unsigned long c;
317     if (utf8) {
318       c = i_utf8_advance(&text, &len);
319       if (c == ~0UL) {
320         i_push_error(0, "invalid UTF8 character");
321         return 0;
322       }
323     }
324     else {
325       c = (unsigned char)*text++;
326       --len;
327     }
328
329     index = FT_Get_Char_Index(handle->face, c);
330     error = FT_Load_Glyph(handle->face, index, loadFlags);
331     if (error) {
332       ft2_push_message(error);
333       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
334                     c, index);
335       return 0;
336     }
337     gm = &handle->face->glyph->metrics;
338     glyph_ascent = gm->horiBearingY / 64;
339     glyph_descent = glyph_ascent - gm->height/64;
340     if (first) {
341       start = gm->horiBearingX / 64;
342       /* handles -ve values properly */
343       ascent = glyph_ascent;
344       descent = glyph_descent;
345       first = 0;
346     }
347
348     if (glyph_ascent > ascent)
349       ascent = glyph_ascent;
350     if (glyph_descent < descent)
351       descent = glyph_descent;
352
353     width += gm->horiAdvance / 64;
354
355     if (len == 0) {
356       /* last character 
357        handle the case where the right the of the character overlaps the 
358        right*/
359       rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
360       if (rightb > 0)
361         rightb = 0;
362     }
363   }
364
365   bbox[BBOX_NEG_WIDTH] = start;
366   bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
367   bbox[BBOX_POS_WIDTH] = width - rightb;
368   bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
369   bbox[BBOX_DESCENT] = descent;
370   bbox[BBOX_ASCENT] = ascent;
371   bbox[BBOX_ADVANCE_WIDTH] = width;
372
373   return BBOX_ADVANCE_WIDTH + 1;
374 }
375
376 /*
377 =item transform_box(FT2_FontHandle *handle, int bbox[4])
378
379 bbox contains coorinates of a the top-left and bottom-right of a bounding 
380 box relative to a point.
381
382 This is then transformed and the values in bbox[4] are the top-left
383 and bottom-right of the new bounding box.
384
385 This is meant to provide the bounding box of a transformed character
386 box.  The problem is that if the character was round and is rotated,
387 the real bounding box isn't going to be much different from the
388 original, but this function will return a _bigger_ bounding box.  I
389 suppose I could work my way through the glyph outline, but that's
390 too much hard work.
391
392 =cut
393 */
394 void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
395   double work[8];
396   double *matrix = handle->matrix;
397   int i;
398   
399   work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
400   work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
401   work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
402   work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
403   work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
404   work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
405   work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
406   work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
407
408   bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
409   bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
410   bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
411   bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
412 }
413
414 /*
415 =item expand_bounds(int bbox[4], int bbox2[4]) 
416
417 Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
418 bounding box in bbox[] that encloses both.
419
420 =cut
421 */
422 static void expand_bounds(int bbox[4], int bbox2[4]) {
423   bbox[0] = i_min(bbox[0], bbox2[0]);
424   bbox[1] = i_min(bbox[1], bbox2[1]);
425   bbox[2] = i_max(bbox[2], bbox2[2]);
426   bbox[3] = i_max(bbox[3], bbox2[3]);
427 }
428
429 /*
430 =item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
431
432 Retrieves bounding box information for the font at the given 
433 character width and height.
434
435 This version finds the rectangular bounding box of the glyphs, with
436 the text as transformed by the transformation matrix.  As with
437 i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
438 the topline to the top-left of the bounding box.  Unlike i_ft2_bbox()
439 this could be near the bottom left corner of the box.
440
441 (bbox[4], bbox[5]) is the offset to the start of the baseline.
442 (bbox[6], bbox[7]) is the offset from the start of the baseline to the
443 end of the baseline.
444
445 Returns non-zero on success.
446
447 =cut
448 */
449 int
450 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, 
451            char const *text, int len, int vlayout, int utf8, int *bbox) {
452   FT_Error error;
453   int width;
454   int index;
455   int first;
456   int ascent = 0, descent = 0;
457   int glyph_ascent, glyph_descent;
458   FT_Glyph_Metrics *gm;
459   int start = 0;
460   int work[4];
461   int bounds[4];
462   double x = 0, y = 0;
463   int i;
464   FT_GlyphSlot slot;
465   int advx, advy;
466   int loadFlags = FT_LOAD_DEFAULT;
467
468   if (vlayout)
469     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
470   if (!handle->hint)
471     loadFlags |= FT_LOAD_NO_HINTING;
472
473   error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64, 
474                            handle->xdpi, handle->ydpi);
475   if (error) {
476     ft2_push_message(error);
477     i_push_error(0, "setting size");
478   }
479
480   first = 1;
481   width = 0;
482   while (len) {
483     unsigned long c;
484     if (utf8) {
485       c = i_utf8_advance(&text, &len);
486       if (c == ~0UL) {
487         i_push_error(0, "invalid UTF8 character");
488         return 0;
489       }
490     }
491     else {
492       c = (unsigned char)*text++;
493       --len;
494     }
495
496     index = FT_Get_Char_Index(handle->face, c);
497     error = FT_Load_Glyph(handle->face, index, loadFlags);
498     if (error) {
499       ft2_push_message(error);
500       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
501                     c, index);
502       return 0;
503     }
504     slot = handle->face->glyph; 
505     gm = &slot->metrics;
506
507     /* these probably don't mean much for vertical layouts */
508     glyph_ascent = gm->horiBearingY / 64;
509     glyph_descent = glyph_ascent - gm->height/64;
510     if (vlayout) {
511       work[0] = gm->vertBearingX;
512       work[1] = gm->vertBearingY;
513     }
514     else {
515       work[0] = gm->horiBearingX;
516       work[1] = gm->horiBearingY;
517     }
518     work[2] = gm->width  + work[0];
519     work[3] = work[1] - gm->height;
520     if (first) {
521       bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
522       bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
523       bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
524       bbox[5] /= 64;
525     }
526     ft2_transform_box(handle, work);
527     for (i = 0; i < 4; ++i)
528       work[i] /= 64;
529     work[0] += x;
530     work[1] += y;
531     work[2] += x;
532     work[3] += y;
533     if (first) {
534       for (i = 0; i < 4; ++i)
535         bounds[i] = work[i];
536       ascent = glyph_ascent;
537       descent = glyph_descent;
538       first = 0;
539     }
540     else {
541       expand_bounds(bounds, work);
542     }
543     x += slot->advance.x / 64;
544     y += slot->advance.y / 64;
545
546     if (glyph_ascent > ascent)
547       ascent = glyph_ascent;
548     if (glyph_descent > descent)
549       descent = glyph_descent;
550
551     if (len == 0) {
552       /* last character 
553        handle the case where the right the of the character overlaps the 
554        right*/
555       /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
556       if (rightb < 0)
557       width -= rightb / 64;*/
558     }
559   }
560
561   /* at this point bounds contains the bounds relative to the CP,
562      and x, y hold the final position relative to the CP */
563   /*bounds[0] -= x;
564   bounds[1] -= y;
565   bounds[2] -= x;
566   bounds[3] -= y;*/
567
568   bbox[0] = bounds[0];
569   bbox[1] = -bounds[3];
570   bbox[2] = bounds[2];
571   bbox[3] = -bounds[1];
572   bbox[6] = x;
573   bbox[7] = -y;
574
575   return 1;
576 }
577
578 static int
579 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
580
581 /*
582 =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)
583
584 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given 
585 I<cheight> and I<cwidth>.
586
587 If align is 0, then the text is rendered with the top-left of the
588 first character at (I<tx>, I<ty>).  If align is non-zero then the text
589 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
590 characters.
591
592 If aa is non-zero then the text is anti-aliased.
593
594 Returns non-zero on success.
595
596 =cut
597 */
598 int
599 i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
600            double cheight, double cwidth, char const *text, int len, int align,
601            int aa, int vlayout, int utf8) {
602   FT_Error error;
603   int index;
604   FT_Glyph_Metrics *gm;
605   int bbox[BOUNDING_BOX_COUNT];
606   FT_GlyphSlot slot;
607   int x, y;
608   unsigned char *bmp;
609   unsigned char map[256];
610   char last_mode = ft_pixel_mode_none; 
611   int last_grays = -1;
612   int ch;
613   i_color pel;
614   int loadFlags = FT_LOAD_DEFAULT;
615
616   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",
617           handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
618
619   if (vlayout) {
620     if (!FT_HAS_VERTICAL(handle->face)) {
621       i_push_error(0, "face has no vertical metrics");
622       return 0;
623     }
624     loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
625   }
626   if (!handle->hint)
627     loadFlags |= FT_LOAD_NO_HINTING;
628
629   /* set the base-line based on the string ascent */
630   if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
631     return 0;
632
633   if (!align) {
634     /* this may need adjustment */
635     tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
636     ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
637   }
638   while (len) {
639     unsigned long c;
640     if (utf8) {
641       c = i_utf8_advance(&text, &len);
642       if (c == ~0UL) {
643         i_push_error(0, "invalid UTF8 character");
644         return 0;
645       }
646     }
647     else {
648       c = (unsigned char)*text++;
649       --len;
650     }
651     
652     index = FT_Get_Char_Index(handle->face, c);
653     error = FT_Load_Glyph(handle->face, index, loadFlags);
654     if (error) {
655       ft2_push_message(error);
656       i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)", 
657                     c, index);
658       return 0;
659     }
660     slot = handle->face->glyph;
661     gm = &slot->metrics;
662
663     error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
664     if (error) {
665       ft2_push_message(error);
666       i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
667       return 0;
668     }
669     if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
670       bmp = slot->bitmap.buffer;
671       for (y = 0; y < slot->bitmap.rows; ++y) {
672         int pos = 0;
673         int bit = 0x80;
674         for (x = 0; x < slot->bitmap.width; ++x) {
675           if (bmp[pos] & bit)
676             i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
677
678           bit >>= 1;
679           if (bit == 0) {
680             bit = 0x80;
681             ++pos;
682           }
683         }
684         bmp += slot->bitmap.pitch;
685       }
686     }
687     else {
688       /* grey scale or something we can treat as greyscale */
689       /* we create a map to convert from the bitmap values to 0-255 */
690       if (last_mode != slot->bitmap.pixel_mode 
691           || last_grays != slot->bitmap.num_grays) {
692         if (!make_bmp_map(&slot->bitmap, map))
693           return 0;
694         last_mode = slot->bitmap.pixel_mode;
695         last_grays = slot->bitmap.num_grays;
696       }
697       
698       bmp = slot->bitmap.buffer;
699       for (y = 0; y < slot->bitmap.rows; ++y) {
700         for (x = 0; x < slot->bitmap.width; ++x) {
701           int value = map[bmp[x]];
702           if (value) {
703             i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
704             for (ch = 0; ch < im->channels; ++ch) {
705               pel.channel[ch] = 
706                 ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
707             }
708             i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
709           }
710         }
711         bmp += slot->bitmap.pitch;
712       }
713     }
714
715     tx += slot->advance.x / 64;
716     ty -= slot->advance.y / 64;
717   }
718
719   return 1;
720 }
721
722 /*
723 =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)
724
725 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given 
726 I<cheight> and I<cwidth>.
727
728 If align is 0, then the text is rendered with the top-left of the
729 first character at (I<tx>, I<ty>).  If align is non-zero then the text
730 is rendered with (I<tx>, I<ty>) aligned with the base-line of the
731 characters.
732
733 If aa is non-zero then the text is anti-aliased.
734
735 Returns non-zero on success.
736
737 =cut
738 */
739
740 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
741          double cheight, double cwidth, char const *text, int len, int align,
742          int aa, int vlayout, int utf8) {
743   int bbox[8];
744   i_img *work;
745   i_color cl, cl2;
746   int x, y;
747
748   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", 
749           handle, im, tx, ty, channel, cheight, cwidth, text, len));
750
751   if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
752     i_push_error(0, "face has no vertical metrics");
753     return 0;
754   }
755
756   if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
757     return 0;
758
759   work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
760   cl.channel[0] = 255;
761   if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth, 
762                   text, len, 1, aa, vlayout, utf8))
763     return 0;
764
765   if (!align) {
766     tx -= bbox[4];
767     ty += bbox[5];
768   }
769   
770   /* render to the specified channel */
771   /* this will be sped up ... */
772   for (y = 0; y < work->ysize; ++y) {
773     for (x = 0; x < work->xsize; ++x) {
774       i_gpix(work, x, y, &cl);
775       i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
776       cl2.channel[channel] = cl.channel[0];
777       i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
778     }
779   }
780   i_img_destroy(work);
781   return 1;
782 }
783
784 /*
785 =item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
786
787 Check if the given characters are defined by the font.
788
789 Returns the number of characters that were checked.
790
791 =cut
792 */
793 int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len, 
794                     int utf8, char *out) {
795   int count = 0;
796   mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
797           handle, text, len, utf8));
798
799   while (len) {
800     unsigned long c;
801     int index;
802     if (utf8) {
803       c = i_utf8_advance(&text, &len);
804       if (c == ~0UL) {
805         i_push_error(0, "invalid UTF8 character");
806         return 0;
807       }
808     }
809     else {
810       c = (unsigned char)*text++;
811       --len;
812     }
813     
814     index = FT_Get_Char_Index(handle->face, c);
815     *out++ = index != 0;
816     ++count;
817   }
818
819   return count;
820 }
821
822 /* uses a method described in fterrors.h to build an error translation
823    function
824 */
825 #undef __FT_ERRORS_H__
826 #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
827 #define FT_ERROR_START_LIST
828 #define FT_ERROR_END_LIST
829
830 /*
831 =back
832
833 =head2 Internal Functions
834
835 These functions are used in the implementation of freetyp2.c and should not
836 (usually cannot) be called from outside it.
837
838 =over
839
840 =item ft2_push_message(int code)
841
842 Pushes an error message corresponding to code onto the error stack.
843
844 =cut
845 */
846 static void ft2_push_message(int code) {
847   char unknown[40];
848
849   switch (code) {
850 #include FT_ERRORS_H
851   }
852
853   sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
854   i_push_error(code, unknown);
855 }
856
857 /*
858 =item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
859
860 Creates a map to convert grey levels from the glyphs bitmap into
861 values scaled 0..255.
862
863 =cut
864 */
865 static int
866 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
867   int scale;
868   int i;
869
870   switch (bitmap->pixel_mode) {
871   case ft_pixel_mode_grays:
872     scale = bitmap->num_grays;
873     break;
874     
875   default:
876     i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
877     return 0;
878   }
879
880   /* build the table */
881   for (i = 0; i < scale; ++i)
882     map[i] = i * 255 / (bitmap->num_grays - 1);
883
884   return 1;
885 }
886
887 int
888 i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
889   char const *name = FT_Get_Postscript_Name(handle->face);
890
891   i_clear_error();
892
893   if (name) {
894     strncpy(name_buf, name, name_buf_size);
895     name_buf[name_buf_size-1] = '\0';
896
897     return strlen(name) + 1;
898   }
899   else {
900     i_push_error(0, "no face name available");
901     *name_buf = '\0';
902
903     return 0;
904   }
905 }
906
907 /* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
908 /* well, I assume FREETYPE_MAJOR is 2, since we're here */
909 #if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
910 #define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
911 #endif
912
913 int
914 i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned char ch, char *name_buf, 
915                  size_t name_buf_size) {
916 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
917   i_clear_error();
918   *name_buf = '\0';
919   i_push_error(0, "FT2 configured without glyph name support");
920
921   return 0;
922 #else
923   i_clear_error();
924
925   if (FT_Has_PS_Glyph_Names(handle->face)) {
926     FT_UInt index = FT_Get_Char_Index(handle->face, ch);
927
928     if (index) {
929       FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf, 
930                                          name_buf_size);
931       if (error) {
932         ft2_push_message(error);
933         *name_buf = '\0';
934         return;
935       }
936       if (*name_buf) {
937         return strlen(name_buf) + 1;
938       }
939       else {
940         return 0;
941       }
942     }
943     else {
944       i_push_error(0, "no glyph for that character");
945       *name_buf = 0;
946       return 0;
947     }
948   }
949   else {
950     i_push_error(0, "no glyph names in font");
951     *name_buf = '\0';
952     return 0;
953   }
954 #endif
955 }
956
957 int
958 i_ft2_can_do_glyph_names(void) {
959 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
960   return 0;
961 #else
962   return 1;
963 #endif
964 }
965
966 int 
967 i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
968 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
969   return 0;
970 #else
971   return FT_Has_PS_Glyph_Names(handle->face);
972 #endif
973 }
974
975 /*
976 =back
977
978 =head1 AUTHOR
979
980 Tony Cook <tony@develop-help.com>, with a fair amount of help from
981 reading the code in font.c.
982
983 =head1 SEE ALSO
984
985 font.c, Imager::Font(3), Imager(3)
986
987 http://www.freetype.org/
988
989 =cut
990 */
991