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