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