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