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