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