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