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