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