]> git.imager.perl.org - imager.git/blob - font.c
allow Imager to be loaded on Windows 98
[imager.git] / font.c
1 #include "imager.h"
2 #include "imrender.h"
3
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #ifdef HAVE_LIBT1
12 #include <t1lib.h>
13 #endif
14
15
16 /*
17 =head1 NAME
18
19 font.c - implements font handling functions for t1 and truetype fonts
20
21 =head1 SYNOPSIS
22
23   i_init_fonts();
24
25   #ifdef HAVE_LIBT1
26   fontnum = i_t1_new(path_to_pfb, path_to_afm);
27   i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
28   rc = i_t1_destroy(fontnum);
29   #endif
30
31   #ifdef HAVE_LIBTT
32   handle = i_tt_new(path_to_ttf);
33   rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
34   i_tt_destroy(handle);
35
36   // and much more
37
38 =head1 DESCRIPTION
39
40 font.c implements font creation, rendering, bounding box functions and
41 more for Imager.
42
43 =head1 FUNCTION REFERENCE
44
45 Some of these functions are internal.
46
47 =over
48
49 =cut
50
51 */
52
53 /* 
54 =item i_init_fonts()
55
56 Initialize font rendering libraries if they are avaliable.
57
58 =cut
59 */
60
61 undef_int 
62 i_init_fonts(int t1log) {
63   mm_log((1,"Initializing fonts\n"));
64
65 #ifdef HAVE_LIBT1
66   i_init_t1(t1log);
67 #endif
68   
69 #ifdef HAVE_LIBTT
70   i_init_tt();
71 #endif
72
73 #ifdef HAVE_FT2
74   if (!i_ft2_init())
75     return 0;
76 #endif
77
78   return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
79 }
80
81
82
83
84 #ifdef HAVE_LIBT1
85
86 static int t1_get_flags(char const *flags);
87 static char *t1_from_utf8(char const *in, int len, int *outlen);
88
89 static void t1_push_error(void);
90
91 static int t1_active_fonts = 0;
92 static int t1_initialized = 0;
93
94 /* 
95 =item i_init_t1(t1log)
96
97 Initializes the t1lib font rendering engine.
98
99 =cut
100 */
101
102 undef_int
103 i_init_t1(int t1log) {
104   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
105   mm_log((1,"init_t1()\n"));
106
107   if (t1_active_fonts) {
108     mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
109     return 1;
110   }
111
112   if (t1_initialized) {
113     T1_CloseLib();
114   }
115   
116   if (t1log)
117     init_flags |= LOGFILE;
118   if ((T1_InitLib(init_flags) == NULL)){
119     mm_log((1,"Initialization of t1lib failed\n"));
120     return(1);
121   }
122   T1_SetLogLevel(T1LOG_DEBUG);
123   i_t1_set_aa(1); /* Default Antialias value */
124
125   ++t1_initialized;
126
127   return(0);
128 }
129
130
131 /* 
132 =item i_close_t1()
133
134 Shuts the t1lib font rendering engine down.
135
136   This it seems that this function is never used.
137
138 =cut
139 */
140
141 void
142 i_close_t1(void) {
143   T1_CloseLib();
144   t1_initialized = 0;
145 }
146
147
148 /*
149 =item i_t1_new(pfb, afm)
150
151 Loads the fonts with the given filenames, returns its font id
152
153  pfb -  path to pfb file for font
154  afm -  path to afm file for font
155
156 =cut
157 */
158
159 int
160 i_t1_new(char *pfb,char *afm) {
161   int font_id;
162
163   mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
164   font_id = T1_AddFont(pfb);
165   if (font_id<0) {
166     mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
167     return font_id;
168   }
169   
170   if (afm != NULL) {
171     mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
172     if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
173   }
174
175   ++t1_active_fonts;
176
177   return font_id;
178 }
179
180 /*
181 =item i_t1_destroy(font_id)
182
183 Frees resources for a t1 font with given font id.
184
185    font_id - number of the font to free
186
187 =cut
188 */
189
190 int
191 i_t1_destroy(int font_id) {
192   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
193
194   --t1_active_fonts;
195
196   return T1_DeleteFont(font_id);
197 }
198
199
200 /*
201 =item i_t1_set_aa(st)
202
203 Sets the antialiasing level of the t1 library.
204
205    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
206
207 =cut
208 */
209
210 void
211 i_t1_set_aa(int st) {
212   int i;
213   unsigned long cst[17];
214   switch(st) {
215   case 0:
216     T1_AASetBitsPerPixel( 8 );
217     T1_AASetLevel( T1_AA_NONE );
218     T1_AANSetGrayValues( 0, 255 );
219     mm_log((1,"setting T1 antialias to none\n"));
220     break;
221   case 1:
222     T1_AASetBitsPerPixel( 8 );
223     T1_AASetLevel( T1_AA_LOW );
224     T1_AASetGrayValues( 0,65,127,191,255 );
225     mm_log((1,"setting T1 antialias to low\n"));
226     break;
227   case 2:
228     T1_AASetBitsPerPixel(8);
229     T1_AASetLevel(T1_AA_HIGH);
230     for(i=0;i<17;i++) cst[i]=(i*255)/16;
231     T1_AAHSetGrayValues( cst );
232     mm_log((1,"setting T1 antialias to high\n"));
233   }
234 }
235
236
237 /* 
238 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
239
240 Interface to text rendering into a single channel in an image
241
242    im        pointer to image structure
243    xb        x coordinate of start of string
244    yb        y coordinate of start of string ( see align )
245    channel - destination channel
246    fontnum - t1 library font id
247    points  - number of points in fontheight
248    str     - string to render
249    len     - string length
250    align   - (0 - top of font glyph | 1 - baseline )
251
252 =cut
253 */
254
255 undef_int
256 i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) {
257   GLYPH *glyph;
258   int xsize,ysize,x,y;
259   i_color val;
260   int mod_flags = t1_get_flags(flags);
261
262   unsigned int ch_mask_store;
263   
264   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
265
266   if (utf8) {
267     int worklen;
268     char *work = t1_from_utf8(str, len, &worklen);
269     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
270     myfree(work);
271   }
272   else {
273     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
274   }
275   if (glyph == NULL)
276     return 0;
277
278   mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
279   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
280   mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
281   mm_log((1,"bpp: %d\n",glyph->bpp));
282   
283   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
284   ysize=glyph->metrics.ascent-glyph->metrics.descent;
285   
286   mm_log((1,"width: %d height: %d\n",xsize,ysize));
287
288   ch_mask_store=im->ch_mask;
289   im->ch_mask=1<<channel;
290
291   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
292   
293   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
294     val.channel[channel]=glyph->bits[y*xsize+x];
295     i_ppix(im,x+xb,y+yb,&val);
296   }
297   
298   im->ch_mask=ch_mask_store;
299   return 1;
300 }
301
302 static void
303 t1_fix_bbox(BBox *bbox, const char *str, int len, int advance, 
304             int space_position) {
305   /* never called with len == 0 */
306   if (str[0] == space_position && bbox->llx > 0)
307     bbox->llx = 0;
308   if (str[len-1] == space_position && bbox->urx < advance)
309     bbox->urx = advance;
310   if (bbox->lly > bbox->ury)
311     bbox->lly = bbox->ury = 0; 
312 }
313
314 /*
315 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
316
317 function to get a strings bounding box given the font id and sizes
318
319    handle  - pointer to font handle   
320    fontnum - t1 library font id
321    points  - number of points in fontheight
322    str     - string to measure
323    len     - string length
324    cords   - the bounding box (modified in place)
325
326 =cut
327 */
328
329 int
330 i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) {
331   BBox bbox;
332   BBox gbbox;
333   int mod_flags = t1_get_flags(flags);
334   int advance;
335   int space_position = T1_GetEncodingIndex(fontnum, "space");
336   
337   mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
338   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
339
340   if (len == 0) {
341     /* len == 0 has special meaning to T1lib, but it means there's
342        nothing to draw, so return that */
343     bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
344     advance = 0;
345   }
346   else {
347     if (utf8) {
348       int worklen;
349       char *work = t1_from_utf8(str, len, &worklen);
350       advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
351       bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
352       t1_fix_bbox(&bbox, work, worklen, advance, space_position);
353       myfree(work);
354     }
355     else {
356       advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
357       bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
358       t1_fix_bbox(&bbox, str, len, advance, space_position);
359     }
360   }
361   gbbox = T1_GetFontBBox(fontnum);
362   
363   mm_log((1,"bbox: (%d,%d,%d,%d)\n",
364           (int)(bbox.llx*points/1000),
365           (int)(gbbox.lly*points/1000),
366           (int)(bbox.urx*points/1000),
367           (int)(gbbox.ury*points/1000),
368           (int)(bbox.lly*points/1000),
369           (int)(bbox.ury*points/1000) ));
370
371
372   cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
373   cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
374
375   cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
376   cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
377
378   cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
379   cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
380
381   cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
382   cords[BBOX_RIGHT_BEARING] = 
383     cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
384
385   return BBOX_RIGHT_BEARING+1;
386 }
387
388
389 /*
390 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
391
392 Interface to text rendering in a single color onto an image
393
394    im      - pointer to image structure
395    xb      - x coordinate of start of string
396    yb      - y coordinate of start of string ( see align )
397    cl      - color to draw the text in
398    fontnum - t1 library font id
399    points  - number of points in fontheight
400    str     - char pointer to string to render
401    len     - string length
402    align   - (0 - top of font glyph | 1 - baseline )
403
404 =cut
405 */
406
407 undef_int
408 i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,int len,int align, int utf8, char const *flags) {
409   GLYPH *glyph;
410   int xsize,ysize,x,y,ch;
411   i_color val;
412   unsigned char c,i;
413   int mod_flags = t1_get_flags(flags);
414   i_render r;
415
416   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
417
418   if (utf8) {
419     int worklen;
420     char *work = t1_from_utf8(str, len, &worklen);
421     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
422     myfree(work);
423   }
424   else {
425     /* T1_AASetString() accepts a char * not a const char */
426     glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
427   }
428   if (glyph == NULL)
429     return 0;
430
431   mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
432   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
433   mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
434   mm_log((1,"bpp: %d\n",glyph->bpp));
435   
436   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
437   ysize=glyph->metrics.ascent-glyph->metrics.descent;
438   
439   mm_log((1,"width: %d height: %d\n",xsize,ysize));
440
441   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
442
443   i_render_init(&r, im, xsize);
444   for(y=0;y<ysize;y++) {
445     i_render_color(&r, xb, yb+y, xsize, glyph->bits+y*xsize, cl);
446   }
447   i_render_done(&r);
448     
449   return 1;
450 }
451
452 /*
453 =item t1_get_flags(flags)
454
455 Processes the characters in I<flags> to create a mod_flags value used
456 by some T1Lib functions.
457
458 =cut
459  */
460 static int
461 t1_get_flags(char const *flags) {
462   int mod_flags = T1_KERNING;
463
464   while (*flags) {
465     switch (*flags++) {
466     case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
467     case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
468     case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
469       /* ignore anything we don't recognize */
470     }
471   }
472
473   return mod_flags;
474 }
475
476 /*
477 =item t1_from_utf8(char const *in, int len, int *outlen)
478
479 Produces an unencoded version of I<in> by dropping any Unicode
480 character over 255.
481
482 Returns a newly allocated buffer which should be freed with myfree().
483 Sets *outlen to the number of bytes used in the output string.
484
485 =cut
486 */
487
488 static char *
489 t1_from_utf8(char const *in, int len, int *outlen) {
490   /* at this point len is from a perl SV, so can't approach MAXINT */
491   char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
492   char *p = out;
493   unsigned long c;
494
495   while (len) {
496     c = i_utf8_advance(&in, &len);
497     if (c == ~0UL) {
498       myfree(out);
499       i_push_error(0, "invalid UTF8 character");
500       return 0;
501     }
502     /* yeah, just drop them */
503     if (c < 0x100) {
504       *p++ = (char)c;
505     }
506   }
507   *p = '\0';
508   *outlen = p - out;
509
510   return out;
511 }
512
513 /*
514 =item i_t1_has_chars(font_num, text, len, utf8, out)
515
516 Check if the given characters are defined by the font.  Note that len
517 is the number of bytes, not the number of characters (when utf8 is
518 non-zero).
519
520 out[char index] will be true if the character exists.
521
522 Accepts UTF-8, but since T1 can only have 256 characters, any chars
523 with values over 255 will simply be returned as false.
524
525 Returns the number of characters that were checked.
526
527 =cut
528 */
529
530 int
531 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
532                char *out) {
533   int count = 0;
534   
535   mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", 
536           font_num, text, len, utf8));
537
538   i_clear_error();
539   if (T1_LoadFont(font_num)) {
540     t1_push_error();
541     return 0;
542   }
543
544   while (len) {
545     unsigned long c;
546     if (utf8) {
547       c = i_utf8_advance(&text, &len);
548       if (c == ~0UL) {
549         i_push_error(0, "invalid UTF8 character");
550         return 0;
551       }
552     }
553     else {
554       c = (unsigned char)*text++;
555       --len;
556     }
557     
558     if (c >= 0x100) {
559       /* limit of 256 characters for T1 */
560       *out++ = 0;
561     }
562     else {
563       char const * name = T1_GetCharName(font_num, (unsigned char)c);
564
565       if (name) {
566         *out++ = strcmp(name, ".notdef") != 0;
567       }
568       else {
569         mm_log((2, "  No name found for character %lx\n", c));
570         *out++ = 0;
571       }
572     }
573     ++count;
574   }
575
576   return count;
577 }
578
579 /*
580 =item i_t1_face_name(font_num, name_buf, name_buf_size)
581
582 Copies the face name of the given C<font_num> to C<name_buf>.  Returns
583 the number of characters required to store the name (which can be
584 larger than C<name_buf_size>, including the space required to store
585 the terminating NUL).
586
587 If name_buf is too small (as specified by name_buf_size) then the name
588 will be truncated.  name_buf will always be NUL termintaed.
589
590 =cut
591 */
592
593 int
594 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
595   char *name;
596
597   T1_errno = 0;
598   if (T1_LoadFont(font_num)) {
599     t1_push_error();
600     return 0;
601   }
602   name = T1_GetFontName(font_num);
603
604   if (name) {
605     strncpy(name_buf, name, name_buf_size);
606     name_buf[name_buf_size-1] = '\0';
607     return strlen(name) + 1;
608   }
609   else {
610     t1_push_error();
611     return 0;
612   }
613 }
614
615 int
616 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, 
617                  size_t name_buf_size) {
618   char *name;
619
620   i_clear_error();
621   if (ch > 0xFF) {
622     return 0;
623   }
624   if (T1_LoadFont(font_num)) {
625     t1_push_error();
626     return 0;
627   }
628   name = T1_GetCharName(font_num, (unsigned char)ch);
629   if (name) {
630     if (strcmp(name, ".notdef")) {
631       strncpy(name_buf, name, name_buf_size);
632       name_buf[name_buf_size-1] = '\0';
633       return strlen(name) + 1;
634     }
635     else {
636       return 0;
637     }
638   }
639   else {
640     t1_push_error();
641     return 0;
642   }
643 }
644
645 static void
646 t1_push_error(void) {
647   switch (T1_errno) {
648   case 0: 
649     i_push_error(0, "No error"); 
650     break;
651
652 #ifdef T1ERR_SCAN_FONT_FORMAT
653   case T1ERR_SCAN_FONT_FORMAT:
654     i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); 
655     break;
656 #endif
657
658 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
659   case T1ERR_SCAN_FILE_OPEN_ERR:
660     i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); 
661     break;
662 #endif
663
664 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
665   case T1ERR_SCAN_OUT_OF_MEMORY:
666     i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); 
667     break;
668 #endif
669
670 #ifdef T1ERR_SCAN_ERROR
671   case T1ERR_SCAN_ERROR:
672     i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); 
673     break;
674 #endif
675
676 #ifdef T1ERR_SCAN_FILE_EOF
677   case T1ERR_SCAN_FILE_EOF:
678     i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); 
679     break;
680 #endif
681
682 #ifdef T1ERR_PATH_ERROR
683   case T1ERR_PATH_ERROR:
684     i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); 
685     break;
686 #endif
687
688 #ifdef T1ERR_PARSE_ERROR
689   case T1ERR_PARSE_ERROR:
690     i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); 
691     break;
692 #endif
693
694 #ifdef T1ERR_TYPE1_ABORT
695   case T1ERR_TYPE1_ABORT:
696     i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); 
697     break;
698 #endif
699
700 #ifdef T1ERR_INVALID_FONTID
701   case T1ERR_INVALID_FONTID:
702     i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); 
703     break;
704 #endif
705
706 #ifdef T1ERR_INVALID_PARAMETER
707   case T1ERR_INVALID_PARAMETER:
708     i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); 
709     break;
710 #endif
711
712 #ifdef T1ERR_OP_NOT_PERMITTED
713   case T1ERR_OP_NOT_PERMITTED:
714     i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); 
715     break;
716 #endif
717
718 #ifdef T1ERR_ALLOC_MEM
719   case T1ERR_ALLOC_MEM:
720     i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); 
721     break;
722 #endif
723
724 #ifdef T1ERR_FILE_OPEN_ERR
725   case T1ERR_FILE_OPEN_ERR:
726     i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); 
727     break;
728 #endif
729
730 #ifdef T1ERR_UNSPECIFIED
731   case T1ERR_UNSPECIFIED:
732     i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); 
733     break;
734 #endif
735
736 #ifdef T1ERR_NO_AFM_DATA
737   case T1ERR_NO_AFM_DATA:
738     i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); 
739     break;
740 #endif
741
742 #ifdef T1ERR_X11
743   case T1ERR_X11:
744     i_push_error(T1ERR_X11, "X11"); 
745     break;
746 #endif
747
748 #ifdef T1ERR_COMPOSITE_CHAR
749   case T1ERR_COMPOSITE_CHAR:
750     i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); 
751     break;
752 #endif
753
754   default:
755     i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
756   }
757 }
758
759 #endif /* HAVE_LIBT1 */
760
761
762 /* Truetype font support */
763 #ifdef HAVE_LIBTT
764
765 /* These are enabled by default when configuring Freetype 1.x
766    I haven't a clue how to reliably detect it at compile time.
767
768    We need a compilation probe in Makefile.PL
769 */
770 #define FTXPOST 1
771 #define FTXERR18 1
772
773 #include <freetype.h>
774 #define TT_CHC 5
775
776 #ifdef FTXPOST
777 #include <ftxpost.h>
778 #endif
779
780 #ifdef FTXERR18
781 #include <ftxerr18.h>
782 #endif
783
784 /* some versions of FT1.x don't seem to define this - it's font defined
785    so it won't change */
786 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
787 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
788 #endif
789
790 /* convert a code point into an index in the glyph cache */
791 #define TT_HASH(x) ((x) & 0xFF)
792
793 typedef struct i_glyph_entry_ {
794   TT_Glyph glyph;
795   unsigned long ch;
796 } i_tt_glyph_entry;
797
798 #define TT_NOCHAR (~0UL)
799
800 struct TT_Instancehandle_ {
801   TT_Instance instance;
802   TT_Instance_Metrics imetrics;
803   TT_Glyph_Metrics gmetrics[256];
804   i_tt_glyph_entry glyphs[256];
805   int smooth;
806   int ptsize;
807   int order;
808 };
809
810 typedef struct TT_Instancehandle_ TT_Instancehandle;
811
812 struct TT_Fonthandle_ {
813   TT_Face face;
814   TT_Face_Properties properties;
815   TT_Instancehandle instanceh[TT_CHC];
816   TT_CharMap char_map;
817 #ifdef FTXPOST
818   int loaded_names;
819   TT_Error load_cond;
820 #endif
821 };
822
823 /* Defines */
824
825 #define USTRCT(x) ((x).z)
826 #define TT_VALID( handle )  ( ( handle ).z != NULL )
827
828 static void i_tt_push_error(TT_Error rc);
829
830 /* Prototypes */
831
832 static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
833 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
834 static void i_tt_done_raster_map( TT_Raster_Map *bit );
835 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
836 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
837 static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
838 static void 
839 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, 
840                    TT_Raster_Map *bit, TT_Raster_Map *small_bit, 
841                    int x_off, int y_off, int smooth );
842 static int
843 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
844                         TT_Raster_Map *small_bit, int cords[6], 
845                         char const* txt, int len, int smooth, int utf8 );
846 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
847 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
848 static  int
849 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], 
850                 float points, char const* txt, int len, int smooth, int utf8 );
851 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
852
853
854 /* static globals needed */
855
856 static TT_Engine    engine;
857 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
858 static int  LTT_hinted = 1;  /* FIXME: this too */
859
860
861 /*
862  * FreeType interface
863  */
864
865
866 /*
867 =item init_tt()
868
869 Initializes the freetype font rendering engine
870
871 =cut
872 */
873
874 undef_int
875 i_init_tt() {
876   TT_Error  error;
877   TT_Byte palette[] = { 0, 64, 127, 191, 255 };
878
879   mm_log((1,"init_tt()\n"));
880   error = TT_Init_FreeType( &engine );
881   if ( error ){
882     mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
883     return(1);
884   }
885
886 #ifdef FTXPOST
887   error = TT_Init_Post_Extension( engine );
888   if (error) {
889     mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
890     return 1;
891   }
892 #endif
893
894   error = TT_Set_Raster_Gray_Palette(engine, palette);
895   if (error) {
896     mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
897     return 1;
898   }
899
900   return(0);
901 }
902
903
904 /* 
905 =item i_tt_get_instance(handle, points, smooth)
906
907 Finds a points+smooth instance or if one doesn't exist in the cache
908 allocates room and returns its cache entry
909
910    fontname - path to the font to load
911    handle   - handle to the font.
912    points   - points of the requested font
913    smooth   - boolean (True: antialias on, False: antialias is off)
914
915 =cut
916 */
917
918 static
919 int
920 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
921   int i,idx;
922   TT_Error error;
923   
924   mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
925           handle,points,smooth));
926   
927   if (smooth == -1) { /* Smooth doesn't matter for this search */
928     for(i=0;i<TT_CHC;i++) {
929       if (handle->instanceh[i].ptsize==points) {
930         mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
931         return i;
932       }
933     }
934     smooth=1; /* We will be adding a font - add it as smooth then */
935   } else { /* Smooth doesn't matter for this search */
936     for(i=0;i<TT_CHC;i++) {
937       if (handle->instanceh[i].ptsize == points 
938           && handle->instanceh[i].smooth == smooth) {
939         mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
940         return i;
941       }
942     }
943   }
944   
945   /* Found the instance in the cache - return the cache index */
946   
947   for(idx=0;idx<TT_CHC;idx++) {
948     if (!(handle->instanceh[idx].order)) break; /* find the lru item */
949   }
950
951   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
952   mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
953           USTRCT(handle->instanceh[idx].instance) ));
954   
955   if ( USTRCT(handle->instanceh[idx].instance) ) {
956     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
957
958     /* Free cached glyphs */
959     for(i=0;i<256;i++)
960       if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
961         TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
962
963     for(i=0;i<256;i++) {
964       handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
965       USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
966     }
967
968     /* Free instance if needed */
969     TT_Done_Instance( handle->instanceh[idx].instance );
970   }
971   
972   /* create and initialize instance */
973   /* FIXME: probably a memory leak on fail */
974   
975   (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || 
976           ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
977           ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
978   
979   if ( error ) {
980     mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
981     return -1;
982   }
983   
984   /* Now that the instance should the inplace we need to lower all of the
985      ru counts and put `this' one with the highest entry */
986   
987   for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
988
989   handle->instanceh[idx].order=TT_CHC-1;
990   handle->instanceh[idx].ptsize=points;
991   handle->instanceh[idx].smooth=smooth;
992   TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
993
994   /* Zero the memory for the glyph storage so they are not thought as
995      cached if they haven't been cached since this new font was loaded */
996
997   for(i=0;i<256;i++) {
998     handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
999     USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
1000   }
1001   
1002   return idx;
1003 }
1004
1005
1006 /*
1007 =item i_tt_new(fontname)
1008
1009 Creates a new font handle object, finds a character map and initialise the
1010 the font handle's cache
1011
1012    fontname - path to the font to load
1013
1014 =cut
1015 */
1016
1017 TT_Fonthandle*
1018 i_tt_new(const char *fontname) {
1019   TT_Error error;
1020   TT_Fonthandle *handle;
1021   unsigned short i,n;
1022   unsigned short platform,encoding;
1023
1024   i_clear_error();
1025   
1026   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1027   
1028   /* allocate memory for the structure */
1029   
1030   handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
1031
1032   /* load the typeface */
1033   error = TT_Open_Face( engine, fontname, &handle->face );
1034   if ( error ) {
1035     if ( error == TT_Err_Could_Not_Open_File ) {
1036       mm_log((1, "Could not find/open %s.\n", fontname ));
1037     }
1038     else {
1039       mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, 
1040               error )); 
1041     }
1042     i_tt_push_error(error);
1043     return NULL;
1044   }
1045   
1046   TT_Get_Face_Properties( handle->face, &(handle->properties) );
1047
1048   /* First, look for a Unicode charmap */
1049   n = handle->properties.num_CharMaps;
1050   USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1051   
1052   for ( i = 0; i < n; i++ ) {
1053     TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
1054     if ( (platform == 3 && encoding == 1 ) 
1055          || (platform == 0 && encoding == 0 ) ) {
1056       mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
1057               platform, encoding));
1058       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1059       break;
1060     }
1061   }
1062   if (!USTRCT(handle->char_map) && n != 0) {
1063     /* just use the first one */
1064     TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1065   }
1066
1067   /* Zero the pointsizes - and ordering */
1068   
1069   for(i=0;i<TT_CHC;i++) {
1070     USTRCT(handle->instanceh[i].instance)=NULL;
1071     handle->instanceh[i].order=i;
1072     handle->instanceh[i].ptsize=0;
1073     handle->instanceh[i].smooth=-1;
1074   }
1075
1076 #ifdef FTXPOST
1077   handle->loaded_names = 0;
1078 #endif
1079
1080   mm_log((1,"i_tt_new <- 0x%X\n",handle));
1081   return handle;
1082 }
1083
1084
1085
1086 /*
1087  * raster map management
1088  */
1089
1090 /* 
1091 =item i_tt_init_raster_map(bit, width, height, smooth)
1092
1093 Allocates internal memory for the bitmap as needed by the parameters (internal)
1094                  
1095    bit    - bitmap to allocate into
1096    width  - width of the bitmap
1097    height - height of the bitmap
1098    smooth - boolean (True: antialias on, False: antialias is off)
1099
1100 =cut
1101 */
1102
1103 static
1104 void
1105 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1106
1107   mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1108   
1109   bit->rows  = height;
1110   bit->width = ( width + 3 ) & -4;
1111   bit->flow  = TT_Flow_Down;
1112   
1113   if ( smooth ) {
1114     bit->cols  = bit->width;
1115     bit->size  = bit->rows * bit->width;
1116   } else {
1117     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
1118     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
1119   }
1120
1121   /* rows can be 0 for some glyphs, for example ' ' */
1122   if (bit->rows && bit->size / bit->rows != bit->cols) {
1123     i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1124             bit->width, bit->rows);
1125   }
1126   
1127   mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %d)\n", bit->width, bit->cols, bit->rows, bit->size ));
1128
1129   bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1130   if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1131 }
1132
1133
1134 /*
1135 =item i_tt_clear_raster_map(bit)
1136
1137 Frees the bitmap data and sets pointer to NULL (internal)
1138                  
1139    bit - bitmap to free
1140
1141 =cut
1142 */
1143
1144 static
1145 void
1146 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1147   myfree( bit->bitmap );
1148   bit->bitmap = NULL;
1149 }
1150
1151
1152 /*
1153 =item i_tt_clear_raster_map(bit)
1154
1155 Clears the specified bitmap (internal)
1156                  
1157    bit - bitmap to zero
1158
1159 =cut
1160 */
1161
1162
1163 static
1164 void
1165 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
1166   memset( bit->bitmap, 0, bit->size );
1167 }
1168
1169
1170 /* 
1171 =item i_tt_blit_or(dst, src, x_off, y_off)
1172
1173 function that blits one raster map into another (internal)
1174                  
1175    dst   - destination bitmap
1176    src   - source bitmap
1177    x_off - x offset into the destination bitmap
1178    y_off - y offset into the destination bitmap
1179
1180 =cut
1181 */
1182
1183 static
1184 void
1185 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1186   int  x,  y;
1187   int  x1, x2, y1, y2;
1188   unsigned char *s, *d;
1189   
1190   x1 = x_off < 0 ? -x_off : 0;
1191   y1 = y_off < 0 ? -y_off : 0;
1192   
1193   x2 = (int)dst->cols - x_off;
1194   if ( x2 > src->cols ) x2 = src->cols;
1195   
1196   y2 = (int)dst->rows - y_off;
1197   if ( y2 > src->rows ) y2 = src->rows;
1198
1199   if ( x1 >= x2 ) return;
1200
1201   /* do the real work now */
1202
1203   for ( y = y1; y < y2; ++y ) {
1204     s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
1205     d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1206     
1207     for ( x = x1; x < x2; ++x ) {
1208       if (*s > *d)
1209         *d = *s;
1210       d++;
1211       s++;
1212     }
1213   }
1214 }
1215
1216 /* useful for debugging */
1217 #if 0
1218
1219 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1220   int x, y;
1221   fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
1222   for (y = 0; y < bit->rows; ++y) {
1223     fprintf(out, "%2d:", y);
1224     for (x = 0; x < bit->cols; ++x) {
1225       if ((x & 7) == 0 && x) putc(' ', out);
1226       fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1227     }
1228     putc('\n', out);
1229   }
1230 }
1231
1232 #endif
1233
1234 /* 
1235 =item i_tt_get_glyph(handle, inst, j) 
1236
1237 Function to see if a glyph exists and if so cache it (internal)
1238                  
1239    handle - pointer to font handle
1240    inst   - font instance
1241    j      - charcode of glyph
1242
1243 =cut
1244 */
1245
1246 static
1247 int
1248 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1249   unsigned short load_flags, code;
1250   TT_Error error;
1251
1252   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1253           handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1254   
1255   /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1256
1257   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1258        && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1259     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1260     return 1;
1261   }
1262
1263   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1264     /* clean up the entry */
1265     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1266     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1267     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1268   }
1269   
1270   /* Ok - it wasn't cached - try to get it in */
1271   load_flags = TTLOAD_SCALE_GLYPH;
1272   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1273   
1274   if ( !TT_VALID(handle->char_map) ) {
1275     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1276     if ( code >= handle->properties.num_Glyphs ) code = 0;
1277   } else code = TT_Char_Index( handle->char_map, j );
1278   
1279   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1280     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1281     i_push_error(error, "TT_New_Glyph()");
1282     return 0;
1283   }
1284   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1285     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1286     /* Don't leak */
1287     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1288     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1289     i_push_error(error, "TT_Load_Glyph()");
1290     return 0;
1291   }
1292
1293   /* At this point the glyph should be allocated and loaded */
1294   handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1295
1296   /* Next get the glyph metrics */
1297   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1298                                 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1299   if (error) {
1300     mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1301     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1302     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1303     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1304     i_push_error(error, "TT_Get_Glyph_Metrics()");
1305     return 0;
1306   }
1307
1308   return 1;
1309 }
1310
1311 /*
1312 =item i_tt_has_chars(handle, text, len, utf8, out)
1313
1314 Check if the given characters are defined by the font.  Note that len
1315 is the number of bytes, not the number of characters (when utf8 is
1316 non-zero).
1317
1318 Returns the number of characters that were checked.
1319
1320 =cut
1321 */
1322
1323 int
1324 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1325                char *out) {
1326   int count = 0;
1327   mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
1328           handle, text, len, utf8));
1329
1330   while (len) {
1331     unsigned long c;
1332     int index;
1333     if (utf8) {
1334       c = i_utf8_advance(&text, &len);
1335       if (c == ~0UL) {
1336         i_push_error(0, "invalid UTF8 character");
1337         return 0;
1338       }
1339     }
1340     else {
1341       c = (unsigned char)*text++;
1342       --len;
1343     }
1344     
1345     if (TT_VALID(handle->char_map)) {
1346       index = TT_Char_Index(handle->char_map, c);
1347     }
1348     else {
1349       index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1350       if (index >= handle->properties.num_Glyphs)
1351         index = 0;
1352     }
1353     *out++ = index != 0;
1354     ++count;
1355   }
1356
1357   return count;
1358 }
1359
1360 /* 
1361 =item i_tt_destroy(handle)
1362
1363 Clears the data taken by a font including all cached data such as
1364 pixmaps and glyphs
1365                  
1366    handle - pointer to font handle
1367
1368 =cut
1369 */
1370
1371 void
1372 i_tt_destroy( TT_Fonthandle *handle) {
1373   TT_Close_Face( handle->face );
1374   myfree( handle );
1375   
1376   /* FIXME: Should these be freed automatically by the library? 
1377
1378   TT_Done_Instance( instance );
1379   void
1380     i_tt_done_glyphs( void ) {
1381     int  i;
1382
1383     if ( !glyphs ) return;
1384     
1385     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1386     free( glyphs );
1387     
1388     glyphs = NULL;
1389   }
1390   */
1391 }
1392
1393
1394 /*
1395  * FreeType Rendering functions
1396  */
1397
1398
1399 /* 
1400 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1401
1402 Renders a single glyph into the bit rastermap (internal)
1403
1404    handle   - pointer to font handle
1405    gmetrics - the metrics for the glyph to be rendered
1406    bit      - large bitmap that is the destination for the text
1407    smallbit - small bitmap that is used only if smooth is true
1408    x_off    - x offset of glyph
1409    y_off    - y offset of glyph
1410    smooth   - boolean (True: antialias on, False: antialias is off)
1411
1412 =cut
1413 */
1414
1415 static
1416 void
1417 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth ) {
1418   
1419   mm_log((1,"i_tt_render_glyph(glyph 0x0%X, gmetrics 0x0%X, bit 0x%X, small_bit 0x%X, x_off %d, y_off %d, smooth %d)\n",
1420           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1421   
1422   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1423   else {
1424     TT_F26Dot6 xmin, ymin, xmax, ymax;
1425
1426     xmin =  gmetrics->bbox.xMin & -64;
1427     ymin =  gmetrics->bbox.yMin & -64;
1428     xmax = (gmetrics->bbox.xMax + 63) & -64;
1429     ymax = (gmetrics->bbox.yMax + 63) & -64;
1430     
1431     i_tt_clear_raster_map( small_bit );
1432     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1433     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1434   }
1435 }
1436
1437
1438 /*
1439 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1440
1441 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1442
1443    handle   - pointer to font handle
1444    inst     - font instance
1445    bit      - large bitmap that is the destination for the text
1446    smallbit - small bitmap that is used only if smooth is true
1447    txt      - string to render
1448    len      - length of the string to render
1449    smooth   - boolean (True: antialias on, False: antialias is off)
1450
1451 =cut
1452 */
1453
1454 static
1455 int
1456 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1457                         TT_Raster_Map *small_bit, int cords[6], 
1458                         char const* txt, int len, int smooth, int utf8 ) {
1459   unsigned long j;
1460   TT_F26Dot6 x,y;
1461   
1462   mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d, utf8 %d)\n",
1463           handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1464   
1465   /* 
1466      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1467   */
1468
1469   x=-cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */
1470   y=-cords[4];
1471   
1472   while (len) {
1473     if (utf8) {
1474       j = i_utf8_advance(&txt, &len);
1475       if (j == ~0UL) {
1476         i_push_error(0, "invalid UTF8 character");
1477         return 0;
1478       }
1479     }
1480     else {
1481       j = (unsigned char)*txt++;
1482       --len;
1483     }
1484     if ( !i_tt_get_glyph(handle,inst,j) ) 
1485       continue;
1486     i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1487                        &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
1488                        small_bit, x, y, smooth );
1489     x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1490   }
1491
1492   return 1;
1493 }
1494
1495
1496 /*
1497  * Functions to render rasters (single channel images) onto images
1498  */
1499
1500 /* 
1501 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1502
1503 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1504
1505    im     - image to dump raster on
1506    bit    - bitmap that contains the text to be dumped to im
1507    xb, yb - coordinates, left edge and baseline
1508    cl     - color to use for text
1509    smooth - boolean (True: antialias on, False: antialias is off)
1510
1511 =cut
1512 */
1513
1514 static
1515 void
1516 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
1517   unsigned char *bmap;
1518   i_color val;
1519   int c, i, ch, x, y;
1520   mm_log((1,"i_tt_dump_raster_map2(im 0x%x, bit 0x%X, xb %d, yb %d, cl 0x%X)\n",im,bit,xb,yb,cl));
1521   
1522   bmap = bit->bitmap;
1523
1524   if ( smooth ) {
1525
1526     i_render r;
1527     i_render_init(&r, im, bit->cols);
1528     for(y=0;y<bit->rows;y++) {
1529 #if 0
1530       for(x=0;x<bit->width;x++) {
1531        c = (unsigned char)bmap[y*(bit->cols)+x];
1532        i=255-c;
1533        i_gpix(im,x+xb,y+yb,&val);
1534        for(ch=0;ch<im->channels;ch++) 
1535          val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255;
1536        i_ppix(im,x+xb,y+yb,&val);
1537       }
1538 #else
1539       i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
1540 #endif
1541     }
1542     i_render_done(&r);
1543   } else {
1544     for(y=0;y<bit->rows;y++) {
1545       unsigned mask = 0x80;
1546       unsigned char *p = bmap + y * bit->cols;
1547
1548       for(x = 0; x < bit->width; x++) {
1549         if (*p & mask) {
1550           i_ppix(im, x+xb, y+yb, cl);
1551         }
1552         mask >>= 1;
1553         if (!mask) {
1554           mask = 0x80;
1555           ++p;
1556         }
1557       }
1558     }
1559
1560   }
1561 }
1562
1563
1564 /*
1565 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1566
1567 Function to dump a raster onto a single channel image in color (internal)
1568
1569    im      - image to dump raster on
1570    bit     - bitmap that contains the text to be dumped to im
1571    xb, yb  - coordinates, left edge and baseline
1572    channel - channel to copy to
1573    smooth  - boolean (True: antialias on, False: antialias is off)
1574
1575 =cut
1576 */
1577
1578 static
1579 void
1580 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
1581   char *bmap;
1582   i_color val;
1583   int c,x,y;
1584   int old_mask = im->ch_mask;
1585   im->ch_mask = 1 << channel;
1586
1587   mm_log((1,"i_tt_dump_raster_channel(im 0x%x, bit 0x%X, xb %d, yb %d, channel %d)\n",im,bit,xb,yb,channel));
1588   
1589   bmap = (char *)bit->bitmap;
1590   
1591   if ( smooth ) {
1592     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1593       c = bmap[y*(bit->cols)+x];
1594       val.channel[channel] = c;
1595       i_ppix(im,x+xb,y+yb,&val);
1596     }
1597   } else {
1598     for(y=0;y<bit->rows;y++) {
1599       unsigned mask = 0x80;
1600       unsigned char *p = bmap + y * bit->cols;
1601
1602       for(x=0;x<bit->width;x++) {
1603         val.channel[channel] = (*p & mask) ? 255 : 0;
1604         i_ppix(im,x+xb,y+yb,&val);
1605         
1606         mask >>= 1;
1607         if (!mask) {
1608           ++p;
1609           mask = 0x80;
1610         }
1611       }
1612     }
1613   }
1614   im->ch_mask = old_mask;
1615 }
1616
1617
1618 /* 
1619 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
1620
1621 interface for generating single channel raster of text (internal)
1622
1623    handle - pointer to font handle
1624    bit    - the bitmap that is allocated, rendered into and NOT freed
1625    cords  - the bounding box (modified in place)
1626    points - font size to use
1627    txt    - string to render
1628    len    - length of the string to render
1629    smooth - boolean (True: antialias on, False: antialias is off)
1630
1631 =cut
1632 */
1633
1634 static
1635 int
1636 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1637   int inst;
1638   int width, height;
1639   TT_Raster_Map small_bit;
1640   
1641   /* find or install an instance */
1642   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
1643     mm_log((1,"i_tt_rasterize: get instance failed\n"));
1644     return 0;
1645   }
1646   
1647   /* calculate bounding box */
1648   if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1649     return 0;
1650     
1651   
1652   width  = cords[2]-cords[0];
1653   height = cords[5]-cords[4];
1654   
1655   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
1656   
1657   i_tt_init_raster_map ( bit, width, height, smooth );
1658   i_tt_clear_raster_map( bit );
1659   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1660   
1661   if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
1662                                smooth, utf8 )) {
1663     if ( smooth ) 
1664       i_tt_done_raster_map( &small_bit );
1665     return 0;
1666   }
1667
1668   if ( smooth ) i_tt_done_raster_map( &small_bit );
1669   return 1;
1670 }
1671
1672
1673
1674 /* 
1675  * Exported text rendering interfaces
1676  */
1677
1678
1679 /*
1680 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1681
1682 Interface to text rendering into a single channel in an image
1683
1684    handle  - pointer to font handle
1685    im      - image to render text on to
1686    xb, yb  - coordinates, left edge and baseline
1687    channel - channel to render into
1688    points  - font size to use
1689    txt     - string to render
1690    len     - length of the string to render
1691    smooth  - boolean (True: antialias on, False: antialias is off)
1692
1693 =cut
1694 */
1695
1696 undef_int
1697 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8, int align ) {
1698
1699   int cords[BOUNDING_BOX_COUNT];
1700   int ascent, st_offset, y;
1701   TT_Raster_Map bit;
1702   
1703   i_clear_error();
1704   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1705   
1706   ascent=cords[BBOX_ASCENT];
1707   st_offset=cords[BBOX_NEG_WIDTH];
1708   y = align ? yb-ascent : yb;
1709
1710   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1711   i_tt_done_raster_map( &bit );
1712
1713   return 1;
1714 }
1715
1716
1717 /* 
1718 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
1719
1720 Interface to text rendering in a single color onto an image
1721
1722    handle  - pointer to font handle
1723    im      - image to render text on to
1724    xb, yb  - coordinates, left edge and baseline
1725    cl      - color to use for text
1726    points  - font size to use
1727    txt     - string to render
1728    len     - length of the string to render
1729    smooth  - boolean (True: antialias on, False: antialias is off)
1730
1731 =cut
1732 */
1733
1734 undef_int
1735 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) {
1736   int cords[BOUNDING_BOX_COUNT];
1737   int ascent, st_offset, y;
1738   TT_Raster_Map bit;
1739
1740   i_clear_error();
1741   
1742   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1743   
1744   ascent=cords[BBOX_ASCENT];
1745   st_offset=cords[BBOX_NEG_WIDTH];
1746   y = align ? yb-ascent : yb;
1747
1748   i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
1749   i_tt_done_raster_map( &bit );
1750
1751   return 1;
1752 }
1753
1754
1755 /*
1756 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
1757
1758 Function to get texts bounding boxes given the instance of the font (internal)
1759
1760    handle - pointer to font handle
1761    inst   -  font instance
1762    txt    -  string to measure
1763    len    -  length of the string to render
1764    cords  - the bounding box (modified in place)
1765
1766 =cut
1767 */
1768
1769 static
1770 undef_int
1771 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1772   int upm, casc, cdesc, first;
1773   
1774   int start    = 0;
1775   int width    = 0;
1776   int gdescent = 0;
1777   int gascent  = 0;
1778   int descent  = 0;
1779   int ascent   = 0;
1780   int rightb   = 0;
1781
1782   unsigned long j;
1783   unsigned char *ustr;
1784   ustr=(unsigned char*)txt;
1785
1786   mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d, utf8 %d)\n",handle,inst,len,txt,len, utf8));
1787
1788   upm     = handle->properties.header->Units_Per_EM;
1789   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1790   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1791   
1792   width   = 0;
1793   start   = 0;
1794   
1795   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1796
1797   first=1;
1798   while (len) {
1799     if (utf8) {
1800       j = i_utf8_advance(&txt, &len);
1801       if (j == ~0UL) {
1802         i_push_error(0, "invalid UTF8 character");
1803         return 0;
1804       }
1805     }
1806     else {
1807       j = (unsigned char)*txt++;
1808       --len;
1809     }
1810     if ( i_tt_get_glyph(handle,inst,j) ) {
1811       TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1812       width += gm->advance   / 64;
1813       casc   = (gm->bbox.yMax+63) / 64;
1814       cdesc  = (gm->bbox.yMin-63) / 64;
1815
1816       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
1817               ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1818
1819       if (first) {
1820         start    = gm->bbox.xMin / 64;
1821         ascent   = (gm->bbox.yMax+63) / 64;
1822         descent  = (gm->bbox.yMin-63) / 64;
1823         first = 0;
1824       }
1825       if (!len) { /* if at end of string */
1826         /* the right-side bearing - in case the right-side of a 
1827            character goes past the right of the advance width,
1828            as is common for italic fonts
1829         */
1830         rightb = gm->advance - gm->bearingX 
1831           - (gm->bbox.xMax - gm->bbox.xMin);
1832         /* fprintf(stderr, "font info last: %d %d %d %d\n", 
1833            gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1834       }
1835
1836       ascent  = (ascent  >  casc ?  ascent : casc );
1837       descent = (descent < cdesc ? descent : cdesc);
1838     }
1839   }
1840   
1841   cords[BBOX_NEG_WIDTH]=start;
1842   cords[BBOX_GLOBAL_DESCENT]=gdescent;
1843   cords[BBOX_POS_WIDTH]=width;
1844   if (rightb < 0)
1845     cords[BBOX_POS_WIDTH] -= rightb / 64;
1846   cords[BBOX_GLOBAL_ASCENT]=gascent;
1847   cords[BBOX_DESCENT]=descent;
1848   cords[BBOX_ASCENT]=ascent;
1849   cords[BBOX_ADVANCE_WIDTH] = width;
1850   cords[BBOX_RIGHT_BEARING] = rightb / 64;
1851
1852   return BBOX_RIGHT_BEARING + 1;
1853 }
1854
1855
1856 /*
1857 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1858
1859 Interface to get a strings bounding box
1860
1861    handle - pointer to font handle
1862    points - font size to use
1863    txt    - string to render
1864    len    - length of the string to render
1865    cords  - the bounding box (modified in place)
1866
1867 =cut
1868 */
1869
1870 undef_int
1871 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8) {
1872   int inst;
1873
1874   i_clear_error();
1875   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1876
1877   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1878     i_push_errorf(0, "i_tt_get_instance(%g)", points);
1879     mm_log((1,"i_tt_text: get instance failed\n"));
1880     return 0;
1881   }
1882
1883   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1884 }
1885
1886 /*
1887 =item i_tt_face_name(handle, name_buf, name_buf_size)
1888
1889 Retrieve's the font's postscript name.
1890
1891 This is complicated by the need to handle encodings and so on.
1892
1893 =cut
1894  */
1895 int
1896 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1897   TT_Face_Properties props;
1898   int name_count;
1899   int i;
1900   TT_UShort platform_id, encoding_id, lang_id, name_id;
1901   TT_UShort name_len;
1902   TT_String *name;
1903   int want_index = -1; /* an acceptable but not perfect name */
1904   int score = 0;
1905
1906   i_clear_error();
1907   
1908   TT_Get_Face_Properties(handle->face, &props);
1909   name_count = props.num_Names;
1910   for (i = 0; i < name_count; ++i) {
1911     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1912                    &name_id);
1913
1914     TT_Get_Name_String(handle->face, i, &name, &name_len);
1915
1916     if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1917         && name_id == TT_NAME_ID_PS_NAME) {
1918       int might_want_index = -1;
1919       int might_score = 0;
1920       if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1921           ||
1922           (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1923         /* exactly what we want */
1924         want_index = i;
1925         break;
1926       }
1927       
1928       if (platform_id == TT_PLATFORM_MICROSOFT
1929           && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1930         /* any english is good */
1931         might_want_index = i;
1932         might_score = 9;
1933       }
1934       /* there might be something in between */
1935       else {
1936         /* anything non-unicode is better than nothing */
1937         might_want_index = i;
1938         might_score = 1;
1939       }
1940       if (might_score > score) {
1941         score = might_score;
1942         want_index = might_want_index;
1943       }
1944     }
1945   }
1946
1947   if (want_index != -1) {
1948     TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1949     
1950     strncpy(name_buf, name, name_buf_size);
1951     name_buf[name_buf_size-1] = '\0';
1952
1953     return strlen(name) + 1;
1954   }
1955   else {
1956     i_push_error(0, "no face name present");
1957     return 0;
1958   }
1959 }
1960
1961 void i_tt_dump_names(TT_Fonthandle *handle) {
1962   TT_Face_Properties props;
1963   int name_count;
1964   int i;
1965   TT_UShort platform_id, encoding_id, lang_id, name_id;
1966   TT_UShort name_len;
1967   TT_String *name;
1968   
1969   TT_Get_Face_Properties(handle->face, &props);
1970   name_count = props.num_Names;
1971   for (i = 0; i < name_count; ++i) {
1972     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1973                    &name_id);
1974     TT_Get_Name_String(handle->face, i, &name, &name_len);
1975
1976     printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1977            encoding_id, lang_id, name_id);
1978     if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1979       printf("(unicode)\n");
1980     }
1981     else {
1982       printf("'%s'\n", name);
1983     }
1984   }
1985   fflush(stdout);
1986 }
1987
1988 int
1989 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
1990                  size_t name_buf_size) {
1991 #ifdef FTXPOST
1992   TT_Error rc;
1993   TT_String *psname;
1994   TT_UShort index;
1995
1996   i_clear_error();
1997
1998   if (!handle->loaded_names) {
1999     TT_Post post;
2000     mm_log((1, "Loading PS Names"));
2001     handle->load_cond = TT_Load_PS_Names(handle->face, &post);
2002     ++handle->loaded_names;
2003   }
2004
2005   if (handle->load_cond) {
2006     i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
2007     return 0;
2008   }
2009   
2010   index = TT_Char_Index(handle->char_map, ch);
2011   if (!index) {
2012     i_push_error(0, "no such character");
2013     return 0;
2014   }
2015
2016   rc = TT_Get_PS_Name(handle->face, index, &psname);
2017
2018   if (rc) {
2019     i_push_error(rc, "error getting name");
2020     return 0;
2021   }
2022
2023   strncpy(name_buf, psname, name_buf_size);
2024   name_buf[name_buf_size-1] = '\0';
2025
2026   return strlen(psname) + 1;
2027 #else
2028   mm_log((1, "FTXPOST extension not enabled\n"));
2029   i_clear_error();
2030   i_push_error(0, "Use of FTXPOST extension disabled");
2031
2032   return 0;
2033 #endif
2034 }
2035
2036 /*
2037 =item i_tt_push_error(code)
2038
2039 Push an error message and code onto the Imager error stack.
2040
2041 =cut
2042 */
2043 static void
2044 i_tt_push_error(TT_Error rc) {
2045 #ifdef FTXERR18
2046   TT_String const *msg = TT_ErrToString18(rc);
2047
2048   i_push_error(rc, msg);
2049 #else
2050   i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
2051 #endif
2052 }
2053
2054 #endif /* HAVE_LIBTT */
2055
2056
2057 /*
2058 =back
2059
2060 =head1 AUTHOR
2061
2062 Arnar M. Hrafnkelsson <addi@umich.edu>
2063
2064 =head1 SEE ALSO
2065
2066 Imager(3)
2067
2068 =cut
2069 */