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