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