]> git.imager.perl.org - imager.git/blob - font.c
[rt.cpan.org #29938] add matrix() method to Imager::Matrix2d
[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   if (i_init_t1(t1log))
67     return 0;
68 #endif
69   
70   return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
71 }
72
73
74
75
76 #ifdef HAVE_LIBT1
77
78 static int t1_get_flags(char const *flags);
79 static char *t1_from_utf8(char const *in, size_t len, int *outlen);
80
81 static void t1_push_error(void);
82
83 static int t1_active_fonts = 0;
84 static int t1_initialized = 0;
85
86 /* 
87 =item i_init_t1(t1log)
88
89 Initializes the t1lib font rendering engine.
90
91 =cut
92 */
93
94 undef_int
95 i_init_t1(int t1log) {
96   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
97   mm_log((1,"init_t1()\n"));
98
99   i_clear_error();
100
101   if (t1_active_fonts) {
102     mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
103     i_push_error(0, "Cannot re-initialize T1 - active fonts");
104     return 1;
105   }
106
107   if (t1_initialized) {
108     T1_CloseLib();
109   }
110   
111   if (t1log)
112     init_flags |= LOGFILE;
113   if ((T1_InitLib(init_flags) == NULL)){
114     mm_log((1,"Initialization of t1lib failed\n"));
115     i_push_error(0, "T1_InitLib failed");
116     return(1);
117   }
118   T1_SetLogLevel(T1LOG_DEBUG);
119   i_t1_set_aa(1); /* Default Antialias value */
120
121   ++t1_initialized;
122
123   return(0);
124 }
125
126
127 /* 
128 =item i_close_t1()
129
130 Shuts the t1lib font rendering engine down.
131
132   This it seems that this function is never used.
133
134 =cut
135 */
136
137 void
138 i_close_t1(void) {
139   T1_CloseLib();
140   t1_initialized = 0;
141 }
142
143
144 /*
145 =item i_t1_new(pfb, afm)
146
147 Loads the fonts with the given filenames, returns its font id
148
149  pfb -  path to pfb file for font
150  afm -  path to afm file for font
151
152 =cut
153 */
154
155 int
156 i_t1_new(char *pfb,char *afm) {
157   int font_id;
158
159   i_clear_error();
160
161   if (!t1_initialized && i_init_t1(0))
162     return -1;
163
164   mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
165   font_id = T1_AddFont(pfb);
166   if (font_id<0) {
167     mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
168     return font_id;
169   }
170   
171   if (afm != NULL) {
172     mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
173     if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
174   }
175
176   ++t1_active_fonts;
177
178   return font_id;
179 }
180
181 /*
182 =item i_t1_destroy(font_id)
183
184 Frees resources for a t1 font with given font id.
185
186    font_id - number of the font to free
187
188 =cut
189 */
190
191 int
192 i_t1_destroy(int font_id) {
193   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
194
195   --t1_active_fonts;
196
197   return T1_DeleteFont(font_id);
198 }
199
200
201 /*
202 =item i_t1_set_aa(st)
203
204 Sets the antialiasing level of the t1 library.
205
206    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
207
208 =cut
209 */
210
211 void
212 i_t1_set_aa(int st) {
213   int i;
214   unsigned long cst[17];
215   switch(st) {
216   case 0:
217     T1_AASetBitsPerPixel( 8 );
218     T1_AASetLevel( T1_AA_NONE );
219     T1_AANSetGrayValues( 0, 255 );
220     mm_log((1,"setting T1 antialias to none\n"));
221     break;
222   case 1:
223     T1_AASetBitsPerPixel( 8 );
224     T1_AASetLevel( T1_AA_LOW );
225     T1_AASetGrayValues( 0,65,127,191,255 );
226     mm_log((1,"setting T1 antialias to low\n"));
227     break;
228   case 2:
229     T1_AASetBitsPerPixel(8);
230     T1_AASetLevel(T1_AA_HIGH);
231     for(i=0;i<17;i++) cst[i]=(i*255)/16;
232     T1_AAHSetGrayValues( cst );
233     mm_log((1,"setting T1 antialias to high\n"));
234   }
235 }
236
237
238 /* 
239 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
240
241 Interface to text rendering into a single channel in an image
242
243    im        pointer to image structure
244    xb        x coordinate of start of string
245    yb        y coordinate of start of string ( see align )
246    channel - destination channel
247    fontnum - t1 library font id
248    points  - number of points in fontheight
249    str     - string to render
250    len     - string length
251    align   - (0 - top of font glyph | 1 - baseline )
252
253 =cut
254 */
255
256 undef_int
257 i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,size_t len,int align, int utf8, char const *flags) {
258   GLYPH *glyph;
259   int xsize,ysize,x,y;
260   i_color val;
261   int mod_flags = t1_get_flags(flags);
262
263   unsigned int ch_mask_store;
264   
265   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
266
267   if (utf8) {
268     int worklen;
269     char *work = t1_from_utf8(str, len, &worklen);
270     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
271     myfree(work);
272   }
273   else {
274     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
275   }
276   if (glyph == NULL)
277     return 0;
278
279   mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
280   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
281   mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
282   mm_log((1,"bpp: %d\n",glyph->bpp));
283   
284   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
285   ysize=glyph->metrics.ascent-glyph->metrics.descent;
286   
287   mm_log((1,"width: %d height: %d\n",xsize,ysize));
288
289   ch_mask_store=im->ch_mask;
290   im->ch_mask=1<<channel;
291
292   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
293   
294   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
295     val.channel[channel]=glyph->bits[y*xsize+x];
296     i_ppix(im,x+xb,y+yb,&val);
297   }
298   
299   im->ch_mask=ch_mask_store;
300   return 1;
301 }
302
303 static void
304 t1_fix_bbox(BBox *bbox, const char *str, size_t len, int advance, 
305             int space_position) {
306   /* never called with len == 0 */
307   if (str[0] == space_position && bbox->llx > 0)
308     bbox->llx = 0;
309   if (str[len-1] == space_position && bbox->urx < advance)
310     bbox->urx = advance;
311   if (bbox->lly > bbox->ury)
312     bbox->lly = bbox->ury = 0; 
313 }
314
315 /*
316 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
317
318 function to get a strings bounding box given the font id and sizes
319
320    handle  - pointer to font handle   
321    fontnum - t1 library font id
322    points  - number of points in fontheight
323    str     - string to measure
324    len     - string length
325    cords   - the bounding box (modified in place)
326
327 =cut
328 */
329
330 int
331 i_t1_bbox(int fontnum,float points,const char *str,size_t len,int cords[6], int utf8,char const *flags) {
332   BBox bbox;
333   BBox gbbox;
334   int mod_flags = t1_get_flags(flags);
335   int advance;
336   int space_position = T1_GetEncodingIndex(fontnum, "space");
337   
338   mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
339   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
340
341   if (len == 0) {
342     /* len == 0 has special meaning to T1lib, but it means there's
343        nothing to draw, so return that */
344     bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
345     advance = 0;
346   }
347   else {
348     if (utf8) {
349       int worklen;
350       char *work = t1_from_utf8(str, len, &worklen);
351       advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
352       bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
353       t1_fix_bbox(&bbox, work, worklen, advance, space_position);
354       myfree(work);
355     }
356     else {
357       advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
358       bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
359       t1_fix_bbox(&bbox, str, len, advance, space_position);
360     }
361   }
362   gbbox = T1_GetFontBBox(fontnum);
363   
364   mm_log((1,"bbox: (%d,%d,%d,%d)\n",
365           (int)(bbox.llx*points/1000),
366           (int)(gbbox.lly*points/1000),
367           (int)(bbox.urx*points/1000),
368           (int)(gbbox.ury*points/1000),
369           (int)(bbox.lly*points/1000),
370           (int)(bbox.ury*points/1000) ));
371
372
373   cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
374   cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
375
376   cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
377   cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
378
379   cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
380   cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
381
382   cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
383   cords[BBOX_RIGHT_BEARING] = 
384     cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
385
386   return BBOX_RIGHT_BEARING+1;
387 }
388
389
390 /*
391 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
392
393 Interface to text rendering in a single color onto an image
394
395    im      - pointer to image structure
396    xb      - x coordinate of start of string
397    yb      - y coordinate of start of string ( see align )
398    cl      - color to draw the text in
399    fontnum - t1 library font id
400    points  - number of points in fontheight
401    str     - char pointer to string to render
402    len     - string length
403    align   - (0 - top of font glyph | 1 - baseline )
404
405 =cut
406 */
407
408 undef_int
409 i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,size_t len,int align, int utf8, char const *flags) {
410   GLYPH *glyph;
411   int xsize,ysize,y;
412   int mod_flags = t1_get_flags(flags);
413   i_render r;
414
415   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
416
417   if (utf8) {
418     int worklen;
419     char *work = t1_from_utf8(str, len, &worklen);
420     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
421     myfree(work);
422   }
423   else {
424     /* T1_AASetString() accepts a char * not a const char */
425     glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
426   }
427   if (glyph == NULL)
428     return 0;
429
430   mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
431   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
432   mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
433   mm_log((1,"bpp: %d\n",glyph->bpp));
434   
435   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
436   ysize=glyph->metrics.ascent-glyph->metrics.descent;
437   
438   mm_log((1,"width: %d height: %d\n",xsize,ysize));
439
440   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
441
442   i_render_init(&r, im, xsize);
443   for(y=0;y<ysize;y++) {
444     i_render_color(&r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
445   }
446   i_render_done(&r);
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, size_t 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, size_t 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, size_t 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, size_t 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, size_t len, int smooth, int utf8 );
850 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[6], int utf8 );
851
852
853 /* static globals needed */
854
855 static int TT_initialized = 0;
856 static TT_Engine    engine;
857 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
858 static int  LTT_hinted = 1;  /* FIXME: this too */
859
860
861 /*
862  * FreeType interface
863  */
864
865
866 /*
867 =item init_tt()
868
869 Initializes the freetype font rendering engine
870
871 =cut
872 */
873
874 undef_int
875 i_init_tt(void) {
876   TT_Error  error;
877   TT_Byte palette[] = { 0, 64, 127, 191, 255 };
878
879   i_clear_error();
880
881   mm_log((1,"init_tt()\n"));
882   error = TT_Init_FreeType( &engine );
883   if ( error ){
884     mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
885     i_tt_push_error(error);
886     i_push_error(0, "Could not initialize freetype 1.x");
887     return(1);
888   }
889
890 #ifdef FTXPOST
891   error = TT_Init_Post_Extension( engine );
892   if (error) {
893     mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
894     
895     i_tt_push_error(error);
896     i_push_error(0, "Could not initialize FT 1.x POST extension");
897     return 1;
898   }
899 #endif
900
901   error = TT_Set_Raster_Gray_Palette(engine, palette);
902   if (error) {
903     mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
904     i_tt_push_error(error);
905     i_push_error(0, "Could not initialize FT 1.x POST extension");
906     return 1;
907   }
908
909   TT_initialized = 1;
910
911   return(0);
912 }
913
914
915 /* 
916 =item i_tt_get_instance(handle, points, smooth)
917
918 Finds a points+smooth instance or if one doesn't exist in the cache
919 allocates room and returns its cache entry
920
921    fontname - path to the font to load
922    handle   - handle to the font.
923    points   - points of the requested font
924    smooth   - boolean (True: antialias on, False: antialias is off)
925
926 =cut
927 */
928
929 static
930 int
931 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
932   int i,idx;
933   TT_Error error;
934   
935   mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
936           handle,points,smooth));
937   
938   if (smooth == -1) { /* Smooth doesn't matter for this search */
939     for(i=0;i<TT_CHC;i++) {
940       if (handle->instanceh[i].ptsize==points) {
941         mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
942         return i;
943       }
944     }
945     smooth=1; /* We will be adding a font - add it as smooth then */
946   } else { /* Smooth doesn't matter for this search */
947     for(i=0;i<TT_CHC;i++) {
948       if (handle->instanceh[i].ptsize == points 
949           && handle->instanceh[i].smooth == smooth) {
950         mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
951         return i;
952       }
953     }
954   }
955   
956   /* Found the instance in the cache - return the cache index */
957   
958   for(idx=0;idx<TT_CHC;idx++) {
959     if (!(handle->instanceh[idx].order)) break; /* find the lru item */
960   }
961
962   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
963   mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
964           USTRCT(handle->instanceh[idx].instance) ));
965   
966   if ( USTRCT(handle->instanceh[idx].instance) ) {
967     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
968
969     /* Free cached glyphs */
970     for(i=0;i<256;i++)
971       if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
972         TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
973
974     for(i=0;i<256;i++) {
975       handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
976       USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
977     }
978
979     /* Free instance if needed */
980     TT_Done_Instance( handle->instanceh[idx].instance );
981   }
982   
983   /* create and initialize instance */
984   /* FIXME: probably a memory leak on fail */
985   
986   (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || 
987           ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
988           ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
989   
990   if ( error ) {
991     mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
992     return -1;
993   }
994   
995   /* Now that the instance should the inplace we need to lower all of the
996      ru counts and put `this' one with the highest entry */
997   
998   for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
999
1000   handle->instanceh[idx].order=TT_CHC-1;
1001   handle->instanceh[idx].ptsize=points;
1002   handle->instanceh[idx].smooth=smooth;
1003   TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
1004
1005   /* Zero the memory for the glyph storage so they are not thought as
1006      cached if they haven't been cached since this new font was loaded */
1007
1008   for(i=0;i<256;i++) {
1009     handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
1010     USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
1011   }
1012   
1013   return idx;
1014 }
1015
1016
1017 /*
1018 =item i_tt_new(fontname)
1019
1020 Creates a new font handle object, finds a character map and initialise the
1021 the font handle's cache
1022
1023    fontname - path to the font to load
1024
1025 =cut
1026 */
1027
1028 TT_Fonthandle*
1029 i_tt_new(const char *fontname) {
1030   TT_Error error;
1031   TT_Fonthandle *handle;
1032   unsigned short i,n;
1033   unsigned short platform,encoding;
1034
1035   if (!TT_initialized && i_init_tt()) {
1036     i_push_error(0, "Could not initialize FT1 engine");
1037     return NULL;
1038   }
1039
1040   i_clear_error();
1041   
1042   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1043   
1044   /* allocate memory for the structure */
1045   
1046   handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
1047
1048   /* load the typeface */
1049   error = TT_Open_Face( engine, fontname, &handle->face );
1050   if ( error ) {
1051     if ( error == TT_Err_Could_Not_Open_File ) {
1052       mm_log((1, "Could not find/open %s.\n", fontname ));
1053     }
1054     else {
1055       mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, 
1056               error )); 
1057     }
1058     i_tt_push_error(error);
1059     return NULL;
1060   }
1061   
1062   TT_Get_Face_Properties( handle->face, &(handle->properties) );
1063
1064   /* First, look for a Unicode charmap */
1065   n = handle->properties.num_CharMaps;
1066   USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1067   
1068   for ( i = 0; i < n; i++ ) {
1069     TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
1070     if ( (platform == 3 && encoding == 1 ) 
1071          || (platform == 0 && encoding == 0 ) ) {
1072       mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
1073               platform, encoding));
1074       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1075       break;
1076     }
1077   }
1078   if (!USTRCT(handle->char_map) && n != 0) {
1079     /* just use the first one */
1080     TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1081   }
1082
1083   /* Zero the pointsizes - and ordering */
1084   
1085   for(i=0;i<TT_CHC;i++) {
1086     USTRCT(handle->instanceh[i].instance)=NULL;
1087     handle->instanceh[i].order=i;
1088     handle->instanceh[i].ptsize=0;
1089     handle->instanceh[i].smooth=-1;
1090   }
1091
1092 #ifdef FTXPOST
1093   handle->loaded_names = 0;
1094 #endif
1095
1096   mm_log((1,"i_tt_new <- 0x%X\n",handle));
1097   return handle;
1098 }
1099
1100
1101
1102 /*
1103  * raster map management
1104  */
1105
1106 /* 
1107 =item i_tt_init_raster_map(bit, width, height, smooth)
1108
1109 Allocates internal memory for the bitmap as needed by the parameters (internal)
1110                  
1111    bit    - bitmap to allocate into
1112    width  - width of the bitmap
1113    height - height of the bitmap
1114    smooth - boolean (True: antialias on, False: antialias is off)
1115
1116 =cut
1117 */
1118
1119 static
1120 void
1121 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1122
1123   mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1124   
1125   bit->rows  = height;
1126   bit->width = ( width + 3 ) & -4;
1127   bit->flow  = TT_Flow_Down;
1128   
1129   if ( smooth ) {
1130     bit->cols  = bit->width;
1131     bit->size  = bit->rows * bit->width;
1132   } else {
1133     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
1134     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
1135   }
1136
1137   /* rows can be 0 for some glyphs, for example ' ' */
1138   if (bit->rows && bit->size / bit->rows != bit->cols) {
1139     i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1140             bit->width, bit->rows);
1141   }
1142   
1143   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 ));
1144
1145   bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1146   if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1147 }
1148
1149
1150 /*
1151 =item i_tt_clear_raster_map(bit)
1152
1153 Frees the bitmap data and sets pointer to NULL (internal)
1154                  
1155    bit - bitmap to free
1156
1157 =cut
1158 */
1159
1160 static
1161 void
1162 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1163   myfree( bit->bitmap );
1164   bit->bitmap = NULL;
1165 }
1166
1167
1168 /*
1169 =item i_tt_clear_raster_map(bit)
1170
1171 Clears the specified bitmap (internal)
1172                  
1173    bit - bitmap to zero
1174
1175 =cut
1176 */
1177
1178
1179 static
1180 void
1181 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
1182   memset( bit->bitmap, 0, bit->size );
1183 }
1184
1185
1186 /* 
1187 =item i_tt_blit_or(dst, src, x_off, y_off)
1188
1189 function that blits one raster map into another (internal)
1190                  
1191    dst   - destination bitmap
1192    src   - source bitmap
1193    x_off - x offset into the destination bitmap
1194    y_off - y offset into the destination bitmap
1195
1196 =cut
1197 */
1198
1199 static
1200 void
1201 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1202   int  x,  y;
1203   int  x1, x2, y1, y2;
1204   unsigned char *s, *d;
1205   
1206   x1 = x_off < 0 ? -x_off : 0;
1207   y1 = y_off < 0 ? -y_off : 0;
1208   
1209   x2 = (int)dst->cols - x_off;
1210   if ( x2 > src->cols ) x2 = src->cols;
1211   
1212   y2 = (int)dst->rows - y_off;
1213   if ( y2 > src->rows ) y2 = src->rows;
1214
1215   if ( x1 >= x2 ) return;
1216
1217   /* do the real work now */
1218
1219   for ( y = y1; y < y2; ++y ) {
1220     s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
1221     d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1222     
1223     for ( x = x1; x < x2; ++x ) {
1224       if (*s > *d)
1225         *d = *s;
1226       d++;
1227       s++;
1228     }
1229   }
1230 }
1231
1232 /* useful for debugging */
1233 #if 0
1234
1235 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1236   int x, y;
1237   fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
1238   for (y = 0; y < bit->rows; ++y) {
1239     fprintf(out, "%2d:", y);
1240     for (x = 0; x < bit->cols; ++x) {
1241       if ((x & 7) == 0 && x) putc(' ', out);
1242       fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1243     }
1244     putc('\n', out);
1245   }
1246 }
1247
1248 #endif
1249
1250 /* 
1251 =item i_tt_get_glyph(handle, inst, j) 
1252
1253 Function to see if a glyph exists and if so cache it (internal)
1254                  
1255    handle - pointer to font handle
1256    inst   - font instance
1257    j      - charcode of glyph
1258
1259 =cut
1260 */
1261
1262 static
1263 int
1264 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1265   unsigned short load_flags, code;
1266   TT_Error error;
1267
1268   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1269           handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1270   
1271   /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1272
1273   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1274        && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1275     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1276     return 1;
1277   }
1278
1279   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1280     /* clean up the entry */
1281     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1282     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1283     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1284   }
1285   
1286   /* Ok - it wasn't cached - try to get it in */
1287   load_flags = TTLOAD_SCALE_GLYPH;
1288   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1289   
1290   if ( !TT_VALID(handle->char_map) ) {
1291     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1292     if ( code >= handle->properties.num_Glyphs ) code = 0;
1293   } else code = TT_Char_Index( handle->char_map, j );
1294   
1295   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1296     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1297     i_push_error(error, "TT_New_Glyph()");
1298     return 0;
1299   }
1300   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1301     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1302     /* Don't leak */
1303     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1304     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1305     i_push_error(error, "TT_Load_Glyph()");
1306     return 0;
1307   }
1308
1309   /* At this point the glyph should be allocated and loaded */
1310   handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1311
1312   /* Next get the glyph metrics */
1313   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1314                                 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1315   if (error) {
1316     mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1317     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1318     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1319     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1320     i_push_error(error, "TT_Get_Glyph_Metrics()");
1321     return 0;
1322   }
1323
1324   return 1;
1325 }
1326
1327 /*
1328 =item i_tt_has_chars(handle, text, len, utf8, out)
1329
1330 Check if the given characters are defined by the font.  Note that len
1331 is the number of bytes, not the number of characters (when utf8 is
1332 non-zero).
1333
1334 Returns the number of characters that were checked.
1335
1336 =cut
1337 */
1338
1339 int
1340 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
1341                char *out) {
1342   int count = 0;
1343   mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
1344           handle, text, len, utf8));
1345
1346   while (len) {
1347     unsigned long c;
1348     int index;
1349     if (utf8) {
1350       c = i_utf8_advance(&text, &len);
1351       if (c == ~0UL) {
1352         i_push_error(0, "invalid UTF8 character");
1353         return 0;
1354       }
1355     }
1356     else {
1357       c = (unsigned char)*text++;
1358       --len;
1359     }
1360     
1361     if (TT_VALID(handle->char_map)) {
1362       index = TT_Char_Index(handle->char_map, c);
1363     }
1364     else {
1365       index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1366       if (index >= handle->properties.num_Glyphs)
1367         index = 0;
1368     }
1369     *out++ = index != 0;
1370     ++count;
1371   }
1372
1373   return count;
1374 }
1375
1376 /* 
1377 =item i_tt_destroy(handle)
1378
1379 Clears the data taken by a font including all cached data such as
1380 pixmaps and glyphs
1381                  
1382    handle - pointer to font handle
1383
1384 =cut
1385 */
1386
1387 void
1388 i_tt_destroy( TT_Fonthandle *handle) {
1389   TT_Close_Face( handle->face );
1390   myfree( handle );
1391   
1392   /* FIXME: Should these be freed automatically by the library? 
1393
1394   TT_Done_Instance( instance );
1395   void
1396     i_tt_done_glyphs( void ) {
1397     int  i;
1398
1399     if ( !glyphs ) return;
1400     
1401     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1402     free( glyphs );
1403     
1404     glyphs = NULL;
1405   }
1406   */
1407 }
1408
1409
1410 /*
1411  * FreeType Rendering functions
1412  */
1413
1414
1415 /* 
1416 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1417
1418 Renders a single glyph into the bit rastermap (internal)
1419
1420    handle   - pointer to font handle
1421    gmetrics - the metrics for the glyph to be rendered
1422    bit      - large bitmap that is the destination for the text
1423    smallbit - small bitmap that is used only if smooth is true
1424    x_off    - x offset of glyph
1425    y_off    - y offset of glyph
1426    smooth   - boolean (True: antialias on, False: antialias is off)
1427
1428 =cut
1429 */
1430
1431 static
1432 void
1433 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 ) {
1434   
1435   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",
1436           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1437   
1438   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1439   else {
1440     TT_F26Dot6 xmin, ymin, xmax, ymax;
1441
1442     xmin =  gmetrics->bbox.xMin & -64;
1443     ymin =  gmetrics->bbox.yMin & -64;
1444     xmax = (gmetrics->bbox.xMax + 63) & -64;
1445     ymax = (gmetrics->bbox.yMax + 63) & -64;
1446     
1447     i_tt_clear_raster_map( small_bit );
1448     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1449     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1450   }
1451 }
1452
1453
1454 /*
1455 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1456
1457 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1458
1459    handle   - pointer to font handle
1460    inst     - font instance
1461    bit      - large bitmap that is the destination for the text
1462    smallbit - small bitmap that is used only if smooth is true
1463    txt      - string to render
1464    len      - length of the string to render
1465    smooth   - boolean (True: antialias on, False: antialias is off)
1466
1467 =cut
1468 */
1469
1470 static
1471 int
1472 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1473                         TT_Raster_Map *small_bit, int cords[6], 
1474                         char const* txt, size_t len, int smooth, int utf8 ) {
1475   unsigned long j;
1476   TT_F26Dot6 x,y;
1477   
1478   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",
1479           handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1480   
1481   /* 
1482      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1483   */
1484
1485   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 */
1486   y=-cords[4];
1487   
1488   while (len) {
1489     if (utf8) {
1490       j = i_utf8_advance(&txt, &len);
1491       if (j == ~0UL) {
1492         i_push_error(0, "invalid UTF8 character");
1493         return 0;
1494       }
1495     }
1496     else {
1497       j = (unsigned char)*txt++;
1498       --len;
1499     }
1500     if ( !i_tt_get_glyph(handle,inst,j) ) 
1501       continue;
1502     i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
1503                        &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
1504                        small_bit, x, y, smooth );
1505     x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1506   }
1507
1508   return 1;
1509 }
1510
1511
1512 /*
1513  * Functions to render rasters (single channel images) onto images
1514  */
1515
1516 /* 
1517 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1518
1519 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1520
1521    im     - image to dump raster on
1522    bit    - bitmap that contains the text to be dumped to im
1523    xb, yb - coordinates, left edge and baseline
1524    cl     - color to use for text
1525    smooth - boolean (True: antialias on, False: antialias is off)
1526
1527 =cut
1528 */
1529
1530 static
1531 void
1532 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
1533   unsigned char *bmap;
1534   int x, y;
1535   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));
1536   
1537   bmap = bit->bitmap;
1538
1539   if ( smooth ) {
1540
1541     i_render r;
1542     i_render_init(&r, im, bit->cols);
1543     for(y=0;y<bit->rows;y++) {
1544 #if 0
1545       for(x=0;x<bit->width;x++) {
1546        c = (unsigned char)bmap[y*(bit->cols)+x];
1547        i=255-c;
1548        i_gpix(im,x+xb,y+yb,&val);
1549        for(ch=0;ch<im->channels;ch++) 
1550          val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255;
1551        i_ppix(im,x+xb,y+yb,&val);
1552       }
1553 #else
1554       i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
1555 #endif
1556     }
1557     i_render_done(&r);
1558   } else {
1559     for(y=0;y<bit->rows;y++) {
1560       unsigned mask = 0x80;
1561       unsigned char *p = bmap + y * bit->cols;
1562
1563       for(x = 0; x < bit->width; x++) {
1564         if (*p & mask) {
1565           i_ppix(im, x+xb, y+yb, cl);
1566         }
1567         mask >>= 1;
1568         if (!mask) {
1569           mask = 0x80;
1570           ++p;
1571         }
1572       }
1573     }
1574
1575   }
1576 }
1577
1578
1579 /*
1580 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1581
1582 Function to dump a raster onto a single channel image in color (internal)
1583
1584    im      - image to dump raster on
1585    bit     - bitmap that contains the text to be dumped to im
1586    xb, yb  - coordinates, left edge and baseline
1587    channel - channel to copy to
1588    smooth  - boolean (True: antialias on, False: antialias is off)
1589
1590 =cut
1591 */
1592
1593 static
1594 void
1595 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
1596   unsigned char *bmap;
1597   i_color val;
1598   int c,x,y;
1599   int old_mask = im->ch_mask;
1600   im->ch_mask = 1 << channel;
1601
1602   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));
1603   
1604   bmap = bit->bitmap;
1605   
1606   if ( smooth ) {
1607     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1608       c = bmap[y*(bit->cols)+x];
1609       val.channel[channel] = c;
1610       i_ppix(im,x+xb,y+yb,&val);
1611     }
1612   } else {
1613     for(y=0;y<bit->rows;y++) {
1614       unsigned mask = 0x80;
1615       unsigned char *p = bmap + y * bit->cols;
1616
1617       for(x=0;x<bit->width;x++) {
1618         val.channel[channel] = (*p & mask) ? 255 : 0;
1619         i_ppix(im,x+xb,y+yb,&val);
1620         
1621         mask >>= 1;
1622         if (!mask) {
1623           ++p;
1624           mask = 0x80;
1625         }
1626       }
1627     }
1628   }
1629   im->ch_mask = old_mask;
1630 }
1631
1632
1633 /* 
1634 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
1635
1636 interface for generating single channel raster of text (internal)
1637
1638    handle - pointer to font handle
1639    bit    - the bitmap that is allocated, rendered into and NOT freed
1640    cords  - the bounding box (modified in place)
1641    points - font size to use
1642    txt    - string to render
1643    len    - length of the string to render
1644    smooth - boolean (True: antialias on, False: antialias is off)
1645
1646 =cut
1647 */
1648
1649 static
1650 int
1651 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, size_t len, int smooth, int utf8 ) {
1652   int inst;
1653   int width, height;
1654   TT_Raster_Map small_bit;
1655   
1656   /* find or install an instance */
1657   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
1658     mm_log((1,"i_tt_rasterize: get instance failed\n"));
1659     return 0;
1660   }
1661   
1662   /* calculate bounding box */
1663   if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1664     return 0;
1665     
1666   
1667   width  = cords[2]-cords[0];
1668   height = cords[5]-cords[4];
1669   
1670   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
1671   
1672   i_tt_init_raster_map ( bit, width, height, smooth );
1673   i_tt_clear_raster_map( bit );
1674   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1675   
1676   if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
1677                                smooth, utf8 )) {
1678     if ( smooth ) 
1679       i_tt_done_raster_map( &small_bit );
1680     return 0;
1681   }
1682
1683   if ( smooth ) i_tt_done_raster_map( &small_bit );
1684   return 1;
1685 }
1686
1687
1688
1689 /* 
1690  * Exported text rendering interfaces
1691  */
1692
1693
1694 /*
1695 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1696
1697 Interface to text rendering into a single channel in an image
1698
1699    handle  - pointer to font handle
1700    im      - image to render text on to
1701    xb, yb  - coordinates, left edge and baseline
1702    channel - channel to render into
1703    points  - font size to use
1704    txt     - string to render
1705    len     - length of the string to render
1706    smooth  - boolean (True: antialias on, False: antialias is off)
1707
1708 =cut
1709 */
1710
1711 undef_int
1712 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, size_t len, int smooth, int utf8, int align ) {
1713
1714   int cords[BOUNDING_BOX_COUNT];
1715   int ascent, st_offset, y;
1716   TT_Raster_Map bit;
1717   
1718   i_clear_error();
1719   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1720   
1721   ascent=cords[BBOX_ASCENT];
1722   st_offset=cords[BBOX_NEG_WIDTH];
1723   y = align ? yb-ascent : yb;
1724
1725   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1726   i_tt_done_raster_map( &bit );
1727
1728   return 1;
1729 }
1730
1731
1732 /* 
1733 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
1734
1735 Interface to text rendering in a single color onto an image
1736
1737    handle  - pointer to font handle
1738    im      - image to render text on to
1739    xb, yb  - coordinates, left edge and baseline
1740    cl      - color to use for text
1741    points  - font size to use
1742    txt     - string to render
1743    len     - length of the string to render
1744    smooth  - boolean (True: antialias on, False: antialias is off)
1745
1746 =cut
1747 */
1748
1749 undef_int
1750 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, size_t len, int smooth, int utf8, int align) {
1751   int cords[BOUNDING_BOX_COUNT];
1752   int ascent, st_offset, y;
1753   TT_Raster_Map bit;
1754
1755   i_clear_error();
1756   
1757   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1758   
1759   ascent=cords[BBOX_ASCENT];
1760   st_offset=cords[BBOX_NEG_WIDTH];
1761   y = align ? yb-ascent : yb;
1762
1763   i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
1764   i_tt_done_raster_map( &bit );
1765
1766   return 1;
1767 }
1768
1769
1770 /*
1771 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
1772
1773 Function to get texts bounding boxes given the instance of the font (internal)
1774
1775    handle - pointer to font handle
1776    inst   -  font instance
1777    txt    -  string to measure
1778    len    -  length of the string to render
1779    cords  - the bounding box (modified in place)
1780
1781 =cut
1782 */
1783
1784 static
1785 undef_int
1786 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1787   int upm, casc, cdesc, first;
1788   
1789   int start    = 0;
1790   int width    = 0;
1791   int gdescent = 0;
1792   int gascent  = 0;
1793   int descent  = 0;
1794   int ascent   = 0;
1795   int rightb   = 0;
1796
1797   unsigned long j;
1798   unsigned char *ustr;
1799   ustr=(unsigned char*)txt;
1800
1801   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));
1802
1803   upm     = handle->properties.header->Units_Per_EM;
1804   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1805   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1806   
1807   width   = 0;
1808   start   = 0;
1809   
1810   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1811
1812   first=1;
1813   while (len) {
1814     if (utf8) {
1815       j = i_utf8_advance(&txt, &len);
1816       if (j == ~0UL) {
1817         i_push_error(0, "invalid UTF8 character");
1818         return 0;
1819       }
1820     }
1821     else {
1822       j = (unsigned char)*txt++;
1823       --len;
1824     }
1825     if ( i_tt_get_glyph(handle,inst,j) ) {
1826       TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1827       width += gm->advance   / 64;
1828       casc   = (gm->bbox.yMax+63) / 64;
1829       cdesc  = (gm->bbox.yMin-63) / 64;
1830
1831       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
1832               ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1833
1834       if (first) {
1835         start    = gm->bbox.xMin / 64;
1836         ascent   = (gm->bbox.yMax+63) / 64;
1837         descent  = (gm->bbox.yMin-63) / 64;
1838         first = 0;
1839       }
1840       if (!len) { /* if at end of string */
1841         /* the right-side bearing - in case the right-side of a 
1842            character goes past the right of the advance width,
1843            as is common for italic fonts
1844         */
1845         rightb = gm->advance - gm->bearingX 
1846           - (gm->bbox.xMax - gm->bbox.xMin);
1847         /* fprintf(stderr, "font info last: %d %d %d %d\n", 
1848            gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1849       }
1850
1851       ascent  = (ascent  >  casc ?  ascent : casc );
1852       descent = (descent < cdesc ? descent : cdesc);
1853     }
1854   }
1855   
1856   cords[BBOX_NEG_WIDTH]=start;
1857   cords[BBOX_GLOBAL_DESCENT]=gdescent;
1858   cords[BBOX_POS_WIDTH]=width;
1859   if (rightb < 0)
1860     cords[BBOX_POS_WIDTH] -= rightb / 64;
1861   cords[BBOX_GLOBAL_ASCENT]=gascent;
1862   cords[BBOX_DESCENT]=descent;
1863   cords[BBOX_ASCENT]=ascent;
1864   cords[BBOX_ADVANCE_WIDTH] = width;
1865   cords[BBOX_RIGHT_BEARING] = rightb / 64;
1866
1867   return BBOX_RIGHT_BEARING + 1;
1868 }
1869
1870
1871 /*
1872 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1873
1874 Interface to get a strings bounding box
1875
1876    handle - pointer to font handle
1877    points - font size to use
1878    txt    - string to render
1879    len    - length of the string to render
1880    cords  - the bounding box (modified in place)
1881
1882 =cut
1883 */
1884
1885 undef_int
1886 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,size_t len,int cords[6], int utf8) {
1887   int inst;
1888
1889   i_clear_error();
1890   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1891
1892   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1893     i_push_errorf(0, "i_tt_get_instance(%g)", points);
1894     mm_log((1,"i_tt_text: get instance failed\n"));
1895     return 0;
1896   }
1897
1898   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1899 }
1900
1901 /*
1902 =item i_tt_face_name(handle, name_buf, name_buf_size)
1903
1904 Retrieve's the font's postscript name.
1905
1906 This is complicated by the need to handle encodings and so on.
1907
1908 =cut
1909  */
1910 int
1911 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1912   TT_Face_Properties props;
1913   int name_count;
1914   int i;
1915   TT_UShort platform_id, encoding_id, lang_id, name_id;
1916   TT_UShort name_len;
1917   TT_String *name;
1918   int want_index = -1; /* an acceptable but not perfect name */
1919   int score = 0;
1920
1921   i_clear_error();
1922   
1923   TT_Get_Face_Properties(handle->face, &props);
1924   name_count = props.num_Names;
1925   for (i = 0; i < name_count; ++i) {
1926     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1927                    &name_id);
1928
1929     TT_Get_Name_String(handle->face, i, &name, &name_len);
1930
1931     if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1932         && name_id == TT_NAME_ID_PS_NAME) {
1933       int might_want_index = -1;
1934       int might_score = 0;
1935       if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1936           ||
1937           (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1938         /* exactly what we want */
1939         want_index = i;
1940         break;
1941       }
1942       
1943       if (platform_id == TT_PLATFORM_MICROSOFT
1944           && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1945         /* any english is good */
1946         might_want_index = i;
1947         might_score = 9;
1948       }
1949       /* there might be something in between */
1950       else {
1951         /* anything non-unicode is better than nothing */
1952         might_want_index = i;
1953         might_score = 1;
1954       }
1955       if (might_score > score) {
1956         score = might_score;
1957         want_index = might_want_index;
1958       }
1959     }
1960   }
1961
1962   if (want_index != -1) {
1963     TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1964     
1965     strncpy(name_buf, name, name_buf_size);
1966     name_buf[name_buf_size-1] = '\0';
1967
1968     return strlen(name) + 1;
1969   }
1970   else {
1971     i_push_error(0, "no face name present");
1972     return 0;
1973   }
1974 }
1975
1976 void i_tt_dump_names(TT_Fonthandle *handle) {
1977   TT_Face_Properties props;
1978   int name_count;
1979   int i;
1980   TT_UShort platform_id, encoding_id, lang_id, name_id;
1981   TT_UShort name_len;
1982   TT_String *name;
1983   
1984   TT_Get_Face_Properties(handle->face, &props);
1985   name_count = props.num_Names;
1986   for (i = 0; i < name_count; ++i) {
1987     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1988                    &name_id);
1989     TT_Get_Name_String(handle->face, i, &name, &name_len);
1990
1991     printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1992            encoding_id, lang_id, name_id);
1993     if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1994       printf("(unicode)\n");
1995     }
1996     else {
1997       printf("'%s'\n", name);
1998     }
1999   }
2000   fflush(stdout);
2001 }
2002
2003 int
2004 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
2005                  size_t name_buf_size) {
2006 #ifdef FTXPOST
2007   TT_Error rc;
2008   TT_String *psname;
2009   TT_UShort index;
2010
2011   i_clear_error();
2012
2013   if (!handle->loaded_names) {
2014     TT_Post post;
2015     mm_log((1, "Loading PS Names"));
2016     handle->load_cond = TT_Load_PS_Names(handle->face, &post);
2017     ++handle->loaded_names;
2018   }
2019
2020   if (handle->load_cond) {
2021     i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
2022     return 0;
2023   }
2024   
2025   index = TT_Char_Index(handle->char_map, ch);
2026   if (!index) {
2027     i_push_error(0, "no such character");
2028     return 0;
2029   }
2030
2031   rc = TT_Get_PS_Name(handle->face, index, &psname);
2032
2033   if (rc) {
2034     i_push_error(rc, "error getting name");
2035     return 0;
2036   }
2037
2038   strncpy(name_buf, psname, name_buf_size);
2039   name_buf[name_buf_size-1] = '\0';
2040
2041   return strlen(psname) + 1;
2042 #else
2043   mm_log((1, "FTXPOST extension not enabled\n"));
2044   i_clear_error();
2045   i_push_error(0, "Use of FTXPOST extension disabled");
2046
2047   return 0;
2048 #endif
2049 }
2050
2051 /*
2052 =item i_tt_push_error(code)
2053
2054 Push an error message and code onto the Imager error stack.
2055
2056 =cut
2057 */
2058 static void
2059 i_tt_push_error(TT_Error rc) {
2060 #ifdef FTXERR18
2061   TT_String const *msg = TT_ErrToString18(rc);
2062
2063   i_push_error(rc, msg);
2064 #else
2065   i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
2066 #endif
2067 }
2068
2069 #endif /* HAVE_LIBTT */
2070
2071
2072 /*
2073 =back
2074
2075 =head1 AUTHOR
2076
2077 Arnar M. Hrafnkelsson <addi@umich.edu>
2078
2079 =head1 SEE ALSO
2080
2081 Imager(3)
2082
2083 =cut
2084 */