]> git.imager.perl.org - imager.git/blob - font.c
t1 no longer needs a type check
[imager.git] / font.c
1 #include "imager.h"
2 #include "imrender.h"
3
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #ifdef HAVE_LIBT1
12 #endif
13
14
15 /*
16 =head1 NAME
17
18 font.c - implements font handling functions for t1 and truetype fonts
19
20 =head1 SYNOPSIS
21
22   i_init_fonts();
23
24   #ifdef HAVE_LIBT1
25   fontnum = i_t1_new(path_to_pfb, path_to_afm);
26   i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
27   rc = i_t1_destroy(fontnum);
28   #endif
29
30   #ifdef HAVE_LIBTT
31   handle = i_tt_new(path_to_ttf);
32   rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
33   i_tt_destroy(handle);
34
35   // and much more
36
37 =head1 DESCRIPTION
38
39 font.c implements font creation, rendering, bounding box functions and
40 more for Imager.
41
42 =head1 FUNCTION REFERENCE
43
44 Some of these functions are internal.
45
46 =over
47
48 =cut
49
50 */
51
52
53 /* Truetype font support */
54 #ifdef HAVE_LIBTT
55
56 /* These are enabled by default when configuring Freetype 1.x
57    I haven't a clue how to reliably detect it at compile time.
58
59    We need a compilation probe in Makefile.PL
60 */
61 #define FTXPOST 1
62 #define FTXERR18 1
63
64 #include <freetype.h>
65 #define TT_CHC 5
66
67 #ifdef FTXPOST
68 #include <ftxpost.h>
69 #endif
70
71 #ifdef FTXERR18
72 #include <ftxerr18.h>
73 #endif
74
75 /* some versions of FT1.x don't seem to define this - it's font defined
76    so it won't change */
77 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
78 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
79 #endif
80
81 /* convert a code point into an index in the glyph cache */
82 #define TT_HASH(x) ((x) & 0xFF)
83
84 typedef struct i_glyph_entry_ {
85   TT_Glyph glyph;
86   unsigned long ch;
87 } i_tt_glyph_entry;
88
89 #define TT_NOCHAR (~0UL)
90
91 struct TT_Instancehandle_ {
92   TT_Instance instance;
93   TT_Instance_Metrics imetrics;
94   TT_Glyph_Metrics gmetrics[256];
95   i_tt_glyph_entry glyphs[256];
96   int smooth;
97   int ptsize;
98   int order;
99 };
100
101 typedef struct TT_Instancehandle_ TT_Instancehandle;
102
103 struct TT_Fonthandle_ {
104   TT_Face face;
105   TT_Face_Properties properties;
106   TT_Instancehandle instanceh[TT_CHC];
107   TT_CharMap char_map;
108 #ifdef FTXPOST
109   int loaded_names;
110   TT_Error load_cond;
111 #endif
112 };
113
114 /* Defines */
115
116 #define USTRCT(x) ((x).z)
117 #define TT_VALID( handle )  ( ( handle ).z != NULL )
118
119 static void i_tt_push_error(TT_Error rc);
120
121 /* Prototypes */
122
123 static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
124 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
125 static void i_tt_done_raster_map( TT_Raster_Map *bit );
126 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
127 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
128 static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
129 static void 
130 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, 
131                    TT_Raster_Map *bit, TT_Raster_Map *small_bit, 
132                    int x_off, int y_off, int smooth );
133 static int
134 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
135                         TT_Raster_Map *small_bit, int cords[6], 
136                         char const* txt, size_t len, int smooth, int utf8 );
137 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
138 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
139 static  int
140 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], 
141                 float points, char const* txt, size_t len, int smooth, int utf8 );
142 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[6], int utf8 );
143
144
145 /* static globals needed */
146
147 static int TT_initialized = 0;
148 static TT_Engine    engine;
149 static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
150 static int  LTT_hinted = 1;  /* FIXME: this too */
151
152
153 /*
154  * FreeType interface
155  */
156
157
158 /*
159 =item init_tt()
160
161 Initializes the freetype font rendering engine
162
163 =cut
164 */
165
166 undef_int
167 i_init_tt(void) {
168   TT_Error  error;
169   TT_Byte palette[] = { 0, 64, 127, 191, 255 };
170
171   i_clear_error();
172
173   mm_log((1,"init_tt()\n"));
174   error = TT_Init_FreeType( &engine );
175   if ( error ){
176     mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
177     i_tt_push_error(error);
178     i_push_error(0, "Could not initialize freetype 1.x");
179     return(1);
180   }
181
182 #ifdef FTXPOST
183   error = TT_Init_Post_Extension( engine );
184   if (error) {
185     mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
186     
187     i_tt_push_error(error);
188     i_push_error(0, "Could not initialize FT 1.x POST extension");
189     return 1;
190   }
191 #endif
192
193   error = TT_Set_Raster_Gray_Palette(engine, palette);
194   if (error) {
195     mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
196     i_tt_push_error(error);
197     i_push_error(0, "Could not initialize FT 1.x POST extension");
198     return 1;
199   }
200
201   TT_initialized = 1;
202
203   return(0);
204 }
205
206
207 /* 
208 =item i_tt_get_instance(handle, points, smooth)
209
210 Finds a points+smooth instance or if one doesn't exist in the cache
211 allocates room and returns its cache entry
212
213    fontname - path to the font to load
214    handle   - handle to the font.
215    points   - points of the requested font
216    smooth   - boolean (True: antialias on, False: antialias is off)
217
218 =cut
219 */
220
221 static
222 int
223 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
224   int i,idx;
225   TT_Error error;
226   
227   mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
228           handle,points,smooth));
229   
230   if (smooth == -1) { /* Smooth doesn't matter for this search */
231     for(i=0;i<TT_CHC;i++) {
232       if (handle->instanceh[i].ptsize==points) {
233         mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
234         return i;
235       }
236     }
237     smooth=1; /* We will be adding a font - add it as smooth then */
238   } else { /* Smooth doesn't matter for this search */
239     for(i=0;i<TT_CHC;i++) {
240       if (handle->instanceh[i].ptsize == points 
241           && handle->instanceh[i].smooth == smooth) {
242         mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
243         return i;
244       }
245     }
246   }
247   
248   /* Found the instance in the cache - return the cache index */
249   
250   for(idx=0;idx<TT_CHC;idx++) {
251     if (!(handle->instanceh[idx].order)) break; /* find the lru item */
252   }
253
254   mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
255   mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
256           USTRCT(handle->instanceh[idx].instance) ));
257   
258   if ( USTRCT(handle->instanceh[idx].instance) ) {
259     mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
260
261     /* Free cached glyphs */
262     for(i=0;i<256;i++)
263       if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
264         TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
265
266     for(i=0;i<256;i++) {
267       handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
268       USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
269     }
270
271     /* Free instance if needed */
272     TT_Done_Instance( handle->instanceh[idx].instance );
273   }
274   
275   /* create and initialize instance */
276   /* FIXME: probably a memory leak on fail */
277   
278   (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || 
279           ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
280           ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
281   
282   if ( error ) {
283     mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
284     return -1;
285   }
286   
287   /* Now that the instance should the inplace we need to lower all of the
288      ru counts and put `this' one with the highest entry */
289   
290   for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
291
292   handle->instanceh[idx].order=TT_CHC-1;
293   handle->instanceh[idx].ptsize=points;
294   handle->instanceh[idx].smooth=smooth;
295   TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
296
297   /* Zero the memory for the glyph storage so they are not thought as
298      cached if they haven't been cached since this new font was loaded */
299
300   for(i=0;i<256;i++) {
301     handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
302     USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
303   }
304   
305   return idx;
306 }
307
308
309 /*
310 =item i_tt_new(fontname)
311
312 Creates a new font handle object, finds a character map and initialise the
313 the font handle's cache
314
315    fontname - path to the font to load
316
317 =cut
318 */
319
320 TT_Fonthandle*
321 i_tt_new(const char *fontname) {
322   TT_Error error;
323   TT_Fonthandle *handle;
324   unsigned short i,n;
325   unsigned short platform,encoding;
326
327   if (!TT_initialized && i_init_tt()) {
328     i_push_error(0, "Could not initialize FT1 engine");
329     return NULL;
330   }
331
332   i_clear_error();
333   
334   mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
335   
336   /* allocate memory for the structure */
337   
338   handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
339
340   /* load the typeface */
341   error = TT_Open_Face( engine, fontname, &handle->face );
342   if ( error ) {
343     if ( error == TT_Err_Could_Not_Open_File ) {
344       mm_log((1, "Could not find/open %s.\n", fontname ));
345     }
346     else {
347       mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, 
348               error )); 
349     }
350     i_tt_push_error(error);
351     return NULL;
352   }
353   
354   TT_Get_Face_Properties( handle->face, &(handle->properties) );
355
356   /* First, look for a Unicode charmap */
357   n = handle->properties.num_CharMaps;
358   USTRCT( handle->char_map )=NULL; /* Invalidate character map */
359   
360   for ( i = 0; i < n; i++ ) {
361     TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
362     if ( (platform == 3 && encoding == 1 ) 
363          || (platform == 0 && encoding == 0 ) ) {
364       mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
365               platform, encoding));
366       TT_Get_CharMap( handle->face, i, &(handle->char_map) );
367       break;
368     }
369   }
370   if (!USTRCT(handle->char_map) && n != 0) {
371     /* just use the first one */
372     TT_Get_CharMap( handle->face, 0, &(handle->char_map));
373   }
374
375   /* Zero the pointsizes - and ordering */
376   
377   for(i=0;i<TT_CHC;i++) {
378     USTRCT(handle->instanceh[i].instance)=NULL;
379     handle->instanceh[i].order=i;
380     handle->instanceh[i].ptsize=0;
381     handle->instanceh[i].smooth=-1;
382   }
383
384 #ifdef FTXPOST
385   handle->loaded_names = 0;
386 #endif
387
388   mm_log((1,"i_tt_new <- 0x%X\n",handle));
389   return handle;
390 }
391
392
393
394 /*
395  * raster map management
396  */
397
398 /* 
399 =item i_tt_init_raster_map(bit, width, height, smooth)
400
401 Allocates internal memory for the bitmap as needed by the parameters (internal)
402                  
403    bit    - bitmap to allocate into
404    width  - width of the bitmap
405    height - height of the bitmap
406    smooth - boolean (True: antialias on, False: antialias is off)
407
408 =cut
409 */
410
411 static
412 void
413 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
414
415   mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
416   
417   bit->rows  = height;
418   bit->width = ( width + 3 ) & -4;
419   bit->flow  = TT_Flow_Down;
420   
421   if ( smooth ) {
422     bit->cols  = bit->width;
423     bit->size  = bit->rows * bit->width;
424   } else {
425     bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
426     bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
427   }
428
429   /* rows can be 0 for some glyphs, for example ' ' */
430   if (bit->rows && bit->size / bit->rows != bit->cols) {
431     i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
432             bit->width, bit->rows);
433   }
434   
435   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 ));
436
437   bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
438   if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
439 }
440
441
442 /*
443 =item i_tt_clear_raster_map(bit)
444
445 Frees the bitmap data and sets pointer to NULL (internal)
446                  
447    bit - bitmap to free
448
449 =cut
450 */
451
452 static
453 void
454 i_tt_done_raster_map( TT_Raster_Map *bit ) {
455   myfree( bit->bitmap );
456   bit->bitmap = NULL;
457 }
458
459
460 /*
461 =item i_tt_clear_raster_map(bit)
462
463 Clears the specified bitmap (internal)
464                  
465    bit - bitmap to zero
466
467 =cut
468 */
469
470
471 static
472 void
473 i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
474   memset( bit->bitmap, 0, bit->size );
475 }
476
477
478 /* 
479 =item i_tt_blit_or(dst, src, x_off, y_off)
480
481 function that blits one raster map into another (internal)
482                  
483    dst   - destination bitmap
484    src   - source bitmap
485    x_off - x offset into the destination bitmap
486    y_off - y offset into the destination bitmap
487
488 =cut
489 */
490
491 static
492 void
493 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
494   int  x,  y;
495   int  x1, x2, y1, y2;
496   unsigned char *s, *d;
497   
498   x1 = x_off < 0 ? -x_off : 0;
499   y1 = y_off < 0 ? -y_off : 0;
500   
501   x2 = (int)dst->cols - x_off;
502   if ( x2 > src->cols ) x2 = src->cols;
503   
504   y2 = (int)dst->rows - y_off;
505   if ( y2 > src->rows ) y2 = src->rows;
506
507   if ( x1 >= x2 ) return;
508
509   /* do the real work now */
510
511   for ( y = y1; y < y2; ++y ) {
512     s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
513     d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
514     
515     for ( x = x1; x < x2; ++x ) {
516       if (*s > *d)
517         *d = *s;
518       d++;
519       s++;
520     }
521   }
522 }
523
524 /* useful for debugging */
525 #if 0
526
527 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
528   int x, y;
529   fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
530   for (y = 0; y < bit->rows; ++y) {
531     fprintf(out, "%2d:", y);
532     for (x = 0; x < bit->cols; ++x) {
533       if ((x & 7) == 0 && x) putc(' ', out);
534       fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
535     }
536     putc('\n', out);
537   }
538 }
539
540 #endif
541
542 /* 
543 =item i_tt_get_glyph(handle, inst, j) 
544
545 Function to see if a glyph exists and if so cache it (internal)
546                  
547    handle - pointer to font handle
548    inst   - font instance
549    j      - charcode of glyph
550
551 =cut
552 */
553
554 static
555 int
556 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
557   unsigned short load_flags, code;
558   TT_Error error;
559
560   mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
561           handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
562   
563   /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
564
565   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
566        && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
567     mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
568     return 1;
569   }
570
571   if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
572     /* clean up the entry */
573     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
574     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
575     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
576   }
577   
578   /* Ok - it wasn't cached - try to get it in */
579   load_flags = TTLOAD_SCALE_GLYPH;
580   if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
581   
582   if ( !TT_VALID(handle->char_map) ) {
583     code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
584     if ( code >= handle->properties.num_Glyphs ) code = 0;
585   } else code = TT_Char_Index( handle->char_map, j );
586   
587   if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
588     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
589     i_push_error(error, "TT_New_Glyph()");
590     return 0;
591   }
592   if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
593     mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
594     /* Don't leak */
595     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
596     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
597     i_push_error(error, "TT_Load_Glyph()");
598     return 0;
599   }
600
601   /* At this point the glyph should be allocated and loaded */
602   handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
603
604   /* Next get the glyph metrics */
605   error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
606                                 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
607   if (error) {
608     mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
609     TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
610     USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
611     handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
612     i_push_error(error, "TT_Get_Glyph_Metrics()");
613     return 0;
614   }
615
616   return 1;
617 }
618
619 /*
620 =item i_tt_has_chars(handle, text, len, utf8, out)
621
622 Check if the given characters are defined by the font.  Note that len
623 is the number of bytes, not the number of characters (when utf8 is
624 non-zero).
625
626 Returns the number of characters that were checked.
627
628 =cut
629 */
630
631 int
632 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
633                char *out) {
634   int count = 0;
635   mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
636           handle, text, len, utf8));
637
638   while (len) {
639     unsigned long c;
640     int index;
641     if (utf8) {
642       c = i_utf8_advance(&text, &len);
643       if (c == ~0UL) {
644         i_push_error(0, "invalid UTF8 character");
645         return 0;
646       }
647     }
648     else {
649       c = (unsigned char)*text++;
650       --len;
651     }
652     
653     if (TT_VALID(handle->char_map)) {
654       index = TT_Char_Index(handle->char_map, c);
655     }
656     else {
657       index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
658       if (index >= handle->properties.num_Glyphs)
659         index = 0;
660     }
661     *out++ = index != 0;
662     ++count;
663   }
664
665   return count;
666 }
667
668 /* 
669 =item i_tt_destroy(handle)
670
671 Clears the data taken by a font including all cached data such as
672 pixmaps and glyphs
673                  
674    handle - pointer to font handle
675
676 =cut
677 */
678
679 void
680 i_tt_destroy( TT_Fonthandle *handle) {
681   TT_Close_Face( handle->face );
682   myfree( handle );
683   
684   /* FIXME: Should these be freed automatically by the library? 
685
686   TT_Done_Instance( instance );
687   void
688     i_tt_done_glyphs( void ) {
689     int  i;
690
691     if ( !glyphs ) return;
692     
693     for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
694     free( glyphs );
695     
696     glyphs = NULL;
697   }
698   */
699 }
700
701
702 /*
703  * FreeType Rendering functions
704  */
705
706
707 /* 
708 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
709
710 Renders a single glyph into the bit rastermap (internal)
711
712    handle   - pointer to font handle
713    gmetrics - the metrics for the glyph to be rendered
714    bit      - large bitmap that is the destination for the text
715    smallbit - small bitmap that is used only if smooth is true
716    x_off    - x offset of glyph
717    y_off    - y offset of glyph
718    smooth   - boolean (True: antialias on, False: antialias is off)
719
720 =cut
721 */
722
723 static
724 void
725 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 ) {
726   
727   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",
728           USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
729   
730   if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
731   else {
732     TT_F26Dot6 xmin, ymin, xmax, ymax;
733
734     xmin =  gmetrics->bbox.xMin & -64;
735     ymin =  gmetrics->bbox.yMin & -64;
736     xmax = (gmetrics->bbox.xMax + 63) & -64;
737     ymax = (gmetrics->bbox.yMax + 63) & -64;
738     
739     i_tt_clear_raster_map( small_bit );
740     TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
741     i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
742   }
743 }
744
745
746 /*
747 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
748
749 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
750
751    handle   - pointer to font handle
752    inst     - font instance
753    bit      - large bitmap that is the destination for the text
754    smallbit - small bitmap that is used only if smooth is true
755    txt      - string to render
756    len      - length of the string to render
757    smooth   - boolean (True: antialias on, False: antialias is off)
758
759 =cut
760 */
761
762 static
763 int
764 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
765                         TT_Raster_Map *small_bit, int cords[6], 
766                         char const* txt, size_t len, int smooth, int utf8 ) {
767   unsigned long j;
768   TT_F26Dot6 x,y;
769   
770   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",
771           handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
772   
773   /* 
774      y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
775   */
776
777   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 */
778   y=-cords[4];
779   
780   while (len) {
781     if (utf8) {
782       j = i_utf8_advance(&txt, &len);
783       if (j == ~0UL) {
784         i_push_error(0, "invalid UTF8 character");
785         return 0;
786       }
787     }
788     else {
789       j = (unsigned char)*txt++;
790       --len;
791     }
792     if ( !i_tt_get_glyph(handle,inst,j) ) 
793       continue;
794     i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
795                        &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
796                        small_bit, x, y, smooth );
797     x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
798   }
799
800   return 1;
801 }
802
803
804 /*
805  * Functions to render rasters (single channel images) onto images
806  */
807
808 /* 
809 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
810
811 Function to dump a raster onto an image in color used by i_tt_text() (internal).
812
813    im     - image to dump raster on
814    bit    - bitmap that contains the text to be dumped to im
815    xb, yb - coordinates, left edge and baseline
816    cl     - color to use for text
817    smooth - boolean (True: antialias on, False: antialias is off)
818
819 =cut
820 */
821
822 static
823 void
824 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
825   unsigned char *bmap;
826   int x, y;
827   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));
828   
829   bmap = bit->bitmap;
830
831   if ( smooth ) {
832
833     i_render r;
834     i_render_init(&r, im, bit->cols);
835     for(y=0;y<bit->rows;y++) {
836 #if 0
837       for(x=0;x<bit->width;x++) {
838        c = (unsigned char)bmap[y*(bit->cols)+x];
839        i=255-c;
840        i_gpix(im,x+xb,y+yb,&val);
841        for(ch=0;ch<im->channels;ch++) 
842          val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255;
843        i_ppix(im,x+xb,y+yb,&val);
844       }
845 #else
846       i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
847 #endif
848     }
849     i_render_done(&r);
850   } else {
851     for(y=0;y<bit->rows;y++) {
852       unsigned mask = 0x80;
853       unsigned char *p = bmap + y * bit->cols;
854
855       for(x = 0; x < bit->width; x++) {
856         if (*p & mask) {
857           i_ppix(im, x+xb, y+yb, cl);
858         }
859         mask >>= 1;
860         if (!mask) {
861           mask = 0x80;
862           ++p;
863         }
864       }
865     }
866
867   }
868 }
869
870
871 /*
872 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
873
874 Function to dump a raster onto a single channel image in color (internal)
875
876    im      - image to dump raster on
877    bit     - bitmap that contains the text to be dumped to im
878    xb, yb  - coordinates, left edge and baseline
879    channel - channel to copy to
880    smooth  - boolean (True: antialias on, False: antialias is off)
881
882 =cut
883 */
884
885 static
886 void
887 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
888   unsigned char *bmap;
889   i_color val;
890   int c,x,y;
891   int old_mask = im->ch_mask;
892   im->ch_mask = 1 << channel;
893
894   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));
895   
896   bmap = bit->bitmap;
897   
898   if ( smooth ) {
899     for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
900       c = bmap[y*(bit->cols)+x];
901       val.channel[channel] = c;
902       i_ppix(im,x+xb,y+yb,&val);
903     }
904   } else {
905     for(y=0;y<bit->rows;y++) {
906       unsigned mask = 0x80;
907       unsigned char *p = bmap + y * bit->cols;
908
909       for(x=0;x<bit->width;x++) {
910         val.channel[channel] = (*p & mask) ? 255 : 0;
911         i_ppix(im,x+xb,y+yb,&val);
912         
913         mask >>= 1;
914         if (!mask) {
915           ++p;
916           mask = 0x80;
917         }
918       }
919     }
920   }
921   im->ch_mask = old_mask;
922 }
923
924
925 /* 
926 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
927
928 interface for generating single channel raster of text (internal)
929
930    handle - pointer to font handle
931    bit    - the bitmap that is allocated, rendered into and NOT freed
932    cords  - the bounding box (modified in place)
933    points - font size to use
934    txt    - string to render
935    len    - length of the string to render
936    smooth - boolean (True: antialias on, False: antialias is off)
937
938 =cut
939 */
940
941 static
942 int
943 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, size_t len, int smooth, int utf8 ) {
944   int inst;
945   int width, height;
946   TT_Raster_Map small_bit;
947   
948   /* find or install an instance */
949   if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
950     mm_log((1,"i_tt_rasterize: get instance failed\n"));
951     return 0;
952   }
953   
954   /* calculate bounding box */
955   if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
956     return 0;
957     
958   
959   width  = cords[2]-cords[0];
960   height = cords[5]-cords[4];
961   
962   mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
963   
964   i_tt_init_raster_map ( bit, width, height, smooth );
965   i_tt_clear_raster_map( bit );
966   if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
967   
968   if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
969                                smooth, utf8 )) {
970     if ( smooth ) 
971       i_tt_done_raster_map( &small_bit );
972     return 0;
973   }
974
975   if ( smooth ) i_tt_done_raster_map( &small_bit );
976   return 1;
977 }
978
979
980
981 /* 
982  * Exported text rendering interfaces
983  */
984
985
986 /*
987 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
988
989 Interface to text rendering into a single channel in an image
990
991    handle  - pointer to font handle
992    im      - image to render text on to
993    xb, yb  - coordinates, left edge and baseline
994    channel - channel to render into
995    points  - font size to use
996    txt     - string to render
997    len     - length of the string to render
998    smooth  - boolean (True: antialias on, False: antialias is off)
999
1000 =cut
1001 */
1002
1003 undef_int
1004 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, size_t len, int smooth, int utf8, int align ) {
1005
1006   int cords[BOUNDING_BOX_COUNT];
1007   int ascent, st_offset, y;
1008   TT_Raster_Map bit;
1009   
1010   i_clear_error();
1011   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1012   
1013   ascent=cords[BBOX_ASCENT];
1014   st_offset=cords[BBOX_NEG_WIDTH];
1015   y = align ? yb-ascent : yb;
1016
1017   i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1018   i_tt_done_raster_map( &bit );
1019
1020   return 1;
1021 }
1022
1023
1024 /* 
1025 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
1026
1027 Interface to text rendering in a single color onto an image
1028
1029    handle  - pointer to font handle
1030    im      - image to render text on to
1031    xb, yb  - coordinates, left edge and baseline
1032    cl      - color to use for text
1033    points  - font size to use
1034    txt     - string to render
1035    len     - length of the string to render
1036    smooth  - boolean (True: antialias on, False: antialias is off)
1037
1038 =cut
1039 */
1040
1041 undef_int
1042 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, size_t len, int smooth, int utf8, int align) {
1043   int cords[BOUNDING_BOX_COUNT];
1044   int ascent, st_offset, y;
1045   TT_Raster_Map bit;
1046
1047   i_clear_error();
1048   
1049   if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1050   
1051   ascent=cords[BBOX_ASCENT];
1052   st_offset=cords[BBOX_NEG_WIDTH];
1053   y = align ? yb-ascent : yb;
1054
1055   i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
1056   i_tt_done_raster_map( &bit );
1057
1058   return 1;
1059 }
1060
1061
1062 /*
1063 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
1064
1065 Function to get texts bounding boxes given the instance of the font (internal)
1066
1067    handle - pointer to font handle
1068    inst   -  font instance
1069    txt    -  string to measure
1070    len    -  length of the string to render
1071    cords  - the bounding box (modified in place)
1072
1073 =cut
1074 */
1075
1076 static
1077 undef_int
1078 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1079   int upm, casc, cdesc, first;
1080   
1081   int start    = 0;
1082   int width    = 0;
1083   int gdescent = 0;
1084   int gascent  = 0;
1085   int descent  = 0;
1086   int ascent   = 0;
1087   int rightb   = 0;
1088
1089   unsigned long j;
1090   unsigned char *ustr;
1091   ustr=(unsigned char*)txt;
1092
1093   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));
1094
1095   upm     = handle->properties.header->Units_Per_EM;
1096   gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1097   gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1098   
1099   width   = 0;
1100   start   = 0;
1101   
1102   mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1103
1104   first=1;
1105   while (len) {
1106     if (utf8) {
1107       j = i_utf8_advance(&txt, &len);
1108       if (j == ~0UL) {
1109         i_push_error(0, "invalid UTF8 character");
1110         return 0;
1111       }
1112     }
1113     else {
1114       j = (unsigned char)*txt++;
1115       --len;
1116     }
1117     if ( i_tt_get_glyph(handle,inst,j) ) {
1118       TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1119       width += gm->advance   / 64;
1120       casc   = (gm->bbox.yMax+63) / 64;
1121       cdesc  = (gm->bbox.yMin-63) / 64;
1122
1123       mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
1124               ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1125
1126       if (first) {
1127         start    = gm->bbox.xMin / 64;
1128         ascent   = (gm->bbox.yMax+63) / 64;
1129         descent  = (gm->bbox.yMin-63) / 64;
1130         first = 0;
1131       }
1132       if (!len) { /* if at end of string */
1133         /* the right-side bearing - in case the right-side of a 
1134            character goes past the right of the advance width,
1135            as is common for italic fonts
1136         */
1137         rightb = gm->advance - gm->bearingX 
1138           - (gm->bbox.xMax - gm->bbox.xMin);
1139         /* fprintf(stderr, "font info last: %d %d %d %d\n", 
1140            gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1141       }
1142
1143       ascent  = (ascent  >  casc ?  ascent : casc );
1144       descent = (descent < cdesc ? descent : cdesc);
1145     }
1146   }
1147   
1148   cords[BBOX_NEG_WIDTH]=start;
1149   cords[BBOX_GLOBAL_DESCENT]=gdescent;
1150   cords[BBOX_POS_WIDTH]=width;
1151   if (rightb < 0)
1152     cords[BBOX_POS_WIDTH] -= rightb / 64;
1153   cords[BBOX_GLOBAL_ASCENT]=gascent;
1154   cords[BBOX_DESCENT]=descent;
1155   cords[BBOX_ASCENT]=ascent;
1156   cords[BBOX_ADVANCE_WIDTH] = width;
1157   cords[BBOX_RIGHT_BEARING] = rightb / 64;
1158
1159   return BBOX_RIGHT_BEARING + 1;
1160 }
1161
1162
1163 /*
1164 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1165
1166 Interface to get a strings bounding box
1167
1168    handle - pointer to font handle
1169    points - font size to use
1170    txt    - string to render
1171    len    - length of the string to render
1172    cords  - the bounding box (modified in place)
1173
1174 =cut
1175 */
1176
1177 undef_int
1178 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,size_t len,int cords[6], int utf8) {
1179   int inst;
1180
1181   i_clear_error();
1182   mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1183
1184   if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1185     i_push_errorf(0, "i_tt_get_instance(%g)", points);
1186     mm_log((1,"i_tt_text: get instance failed\n"));
1187     return 0;
1188   }
1189
1190   return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1191 }
1192
1193 /*
1194 =item i_tt_face_name(handle, name_buf, name_buf_size)
1195
1196 Retrieve's the font's postscript name.
1197
1198 This is complicated by the need to handle encodings and so on.
1199
1200 =cut
1201  */
1202 int
1203 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1204   TT_Face_Properties props;
1205   int name_count;
1206   int i;
1207   TT_UShort platform_id, encoding_id, lang_id, name_id;
1208   TT_UShort name_len;
1209   TT_String *name;
1210   int want_index = -1; /* an acceptable but not perfect name */
1211   int score = 0;
1212
1213   i_clear_error();
1214   
1215   TT_Get_Face_Properties(handle->face, &props);
1216   name_count = props.num_Names;
1217   for (i = 0; i < name_count; ++i) {
1218     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1219                    &name_id);
1220
1221     TT_Get_Name_String(handle->face, i, &name, &name_len);
1222
1223     if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1224         && name_id == TT_NAME_ID_PS_NAME) {
1225       int might_want_index = -1;
1226       int might_score = 0;
1227       if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1228           ||
1229           (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1230         /* exactly what we want */
1231         want_index = i;
1232         break;
1233       }
1234       
1235       if (platform_id == TT_PLATFORM_MICROSOFT
1236           && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1237         /* any english is good */
1238         might_want_index = i;
1239         might_score = 9;
1240       }
1241       /* there might be something in between */
1242       else {
1243         /* anything non-unicode is better than nothing */
1244         might_want_index = i;
1245         might_score = 1;
1246       }
1247       if (might_score > score) {
1248         score = might_score;
1249         want_index = might_want_index;
1250       }
1251     }
1252   }
1253
1254   if (want_index != -1) {
1255     TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1256     
1257     strncpy(name_buf, name, name_buf_size);
1258     name_buf[name_buf_size-1] = '\0';
1259
1260     return strlen(name) + 1;
1261   }
1262   else {
1263     i_push_error(0, "no face name present");
1264     return 0;
1265   }
1266 }
1267
1268 void i_tt_dump_names(TT_Fonthandle *handle) {
1269   TT_Face_Properties props;
1270   int name_count;
1271   int i;
1272   TT_UShort platform_id, encoding_id, lang_id, name_id;
1273   TT_UShort name_len;
1274   TT_String *name;
1275   
1276   TT_Get_Face_Properties(handle->face, &props);
1277   name_count = props.num_Names;
1278   for (i = 0; i < name_count; ++i) {
1279     TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
1280                    &name_id);
1281     TT_Get_Name_String(handle->face, i, &name, &name_len);
1282
1283     printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1284            encoding_id, lang_id, name_id);
1285     if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1286       printf("(unicode)\n");
1287     }
1288     else {
1289       printf("'%s'\n", name);
1290     }
1291   }
1292   fflush(stdout);
1293 }
1294
1295 int
1296 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
1297                  size_t name_buf_size) {
1298 #ifdef FTXPOST
1299   TT_Error rc;
1300   TT_String *psname;
1301   TT_UShort index;
1302
1303   i_clear_error();
1304
1305   if (!handle->loaded_names) {
1306     TT_Post post;
1307     mm_log((1, "Loading PS Names"));
1308     handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1309     ++handle->loaded_names;
1310   }
1311
1312   if (handle->load_cond) {
1313     i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
1314     return 0;
1315   }
1316   
1317   index = TT_Char_Index(handle->char_map, ch);
1318   if (!index) {
1319     i_push_error(0, "no such character");
1320     return 0;
1321   }
1322
1323   rc = TT_Get_PS_Name(handle->face, index, &psname);
1324
1325   if (rc) {
1326     i_push_error(rc, "error getting name");
1327     return 0;
1328   }
1329
1330   strncpy(name_buf, psname, name_buf_size);
1331   name_buf[name_buf_size-1] = '\0';
1332
1333   return strlen(psname) + 1;
1334 #else
1335   mm_log((1, "FTXPOST extension not enabled\n"));
1336   i_clear_error();
1337   i_push_error(0, "Use of FTXPOST extension disabled");
1338
1339   return 0;
1340 #endif
1341 }
1342
1343 /*
1344 =item i_tt_push_error(code)
1345
1346 Push an error message and code onto the Imager error stack.
1347
1348 =cut
1349 */
1350 static void
1351 i_tt_push_error(TT_Error rc) {
1352 #ifdef FTXERR18
1353   TT_String const *msg = TT_ErrToString18(rc);
1354
1355   i_push_error(rc, msg);
1356 #else
1357   i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1358 #endif
1359 }
1360
1361 #endif /* HAVE_LIBTT */
1362
1363
1364 /*
1365 =back
1366
1367 =head1 AUTHOR
1368
1369 Arnar M. Hrafnkelsson <addi@umich.edu>
1370
1371 =head1 SEE ALSO
1372
1373 Imager(3)
1374
1375 =cut
1376 */