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