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