Modified i_line_aa to use a modified bresenham algorithm. This method
[imager.git] / font.c
CommitLineData
02d1d628
AMH
1#include "image.h"
2
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <fcntl.h>
6
7#include <stdio.h>
8#include <stdlib.h>
9
10
11
12
13
14
15/*
16=head1 NAME
17
18font.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]);
33 i_tt_destroy(handle);
34
35 // and much more
36
37=head1 DESCRIPTION
38
39font.c implements font creation, rendering, bounding box functions and
40more for Imager.
41
42=head1 FUNCTION REFERENCE
43
44Some of these functions are internal.
45
b8c2033e 46=over
02d1d628
AMH
47
48=cut
49
50*/
51
52
53
54
55
56
57
58
59
60/*
61=item i_init_fonts()
62
63Initialize font rendering libraries if they are avaliable.
64
65=cut
66*/
67
68undef_int
4cb58f1b 69i_init_fonts(int t1log) {
02d1d628
AMH
70 mm_log((1,"Initializing fonts\n"));
71
72#ifdef HAVE_LIBT1
b33c08f8 73 i_init_t1(t1log);
02d1d628
AMH
74#endif
75
76#ifdef HAVE_LIBTT
b33c08f8 77 i_init_tt();
02d1d628
AMH
78#endif
79
faa9b3e7
TC
80#ifdef HAVE_FT2
81 if (!i_ft2_init())
82 return 0;
83#endif
84
02d1d628
AMH
85 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
86}
87
88
89
90
91#ifdef HAVE_LIBT1
92
93
94
95/*
4cb58f1b 96=item i_init_t1(t1log)
02d1d628
AMH
97
98Initializes the t1lib font rendering engine.
99
100=cut
101*/
102
103undef_int
b33c08f8 104i_init_t1(int t1log) {
4cb58f1b 105 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
02d1d628 106 mm_log((1,"init_t1()\n"));
4cb58f1b
TC
107
108 if (t1log)
109 init_flags |= LOGFILE;
110 if ((T1_InitLib(init_flags) == NULL)){
02d1d628
AMH
111 mm_log((1,"Initialization of t1lib failed\n"));
112 return(1);
113 }
114 T1_SetLogLevel(T1LOG_DEBUG);
115 i_t1_set_aa(1); /* Default Antialias value */
116 return(0);
117}
118
119
120/*
121=item i_close_t1()
122
123Shuts the t1lib font rendering engine down.
124
125 This it seems that this function is never used.
126
127=cut
128*/
129
130void
faa9b3e7 131i_close_t1(void) {
02d1d628
AMH
132 T1_CloseLib();
133}
134
135
136/*
137=item i_t1_new(pfb, afm)
138
139Loads the fonts with the given filenames, returns its font id
140
141 pfb - path to pfb file for font
142 afm - path to afm file for font
143
144=cut
145*/
146
147int
148i_t1_new(char *pfb,char *afm) {
149 int font_id;
150 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
151 font_id = T1_AddFont(pfb);
152 if (font_id<0) {
153 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
154 return font_id;
155 }
156
157 if (afm != NULL) {
158 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
159 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
160 }
161 return font_id;
162}
163
164/*
165=item i_t1_destroy(font_id)
166
167Frees resources for a t1 font with given font id.
168
169 font_id - number of the font to free
170
171=cut
172*/
173
174int
175i_t1_destroy(int font_id) {
176 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
177 return T1_DeleteFont(font_id);
178}
179
180
181/*
182=item i_t1_set_aa(st)
183
184Sets the antialiasing level of the t1 library.
185
186 st - 0 = NONE, 1 = LOW, 2 = HIGH.
187
188=cut
189*/
190
191void
192i_t1_set_aa(int st) {
193 int i;
194 unsigned long cst[17];
195 switch(st) {
196 case 0:
197 T1_AASetBitsPerPixel( 8 );
198 T1_AASetLevel( T1_AA_NONE );
199 T1_AANSetGrayValues( 0, 255 );
200 mm_log((1,"setting T1 antialias to none\n"));
201 break;
202 case 1:
203 T1_AASetBitsPerPixel( 8 );
204 T1_AASetLevel( T1_AA_LOW );
205 T1_AASetGrayValues( 0,65,127,191,255 );
206 mm_log((1,"setting T1 antialias to low\n"));
207 break;
208 case 2:
209 T1_AASetBitsPerPixel(8);
210 T1_AASetLevel(T1_AA_HIGH);
211 for(i=0;i<17;i++) cst[i]=(i*255)/16;
212 T1_AAHSetGrayValues( cst );
213 mm_log((1,"setting T1 antialias to high\n"));
214 }
215}
216
217
218/*
219=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
220
221Interface to text rendering into a single channel in an image
222
223 im pointer to image structure
224 xb x coordinate of start of string
225 yb y coordinate of start of string ( see align )
226 channel - destination channel
227 fontnum - t1 library font id
228 points - number of points in fontheight
229 str - string to render
230 len - string length
231 align - (0 - top of font glyph | 1 - baseline )
232
233=cut
234*/
235
236undef_int
237i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align) {
238 GLYPH *glyph;
239 int xsize,ysize,x,y;
240 i_color val;
241
242 unsigned int ch_mask_store;
243
244 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
245
246 glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
faa9b3e7
TC
247 if (glyph == NULL)
248 return 0;
02d1d628
AMH
249
250 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
251 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
252 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
253 mm_log((1,"bpp: %d\n",glyph->bpp));
254
255 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
256 ysize=glyph->metrics.ascent-glyph->metrics.descent;
257
258 mm_log((1,"width: %d height: %d\n",xsize,ysize));
259
260 ch_mask_store=im->ch_mask;
261 im->ch_mask=1<<channel;
262
263 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
264
265 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
266 val.channel[channel]=glyph->bits[y*xsize+x];
267 i_ppix(im,x+xb,y+yb,&val);
268 }
269
270 im->ch_mask=ch_mask_store;
271 return 1;
272}
273
274
275/*
276=item i_t1_bbox(handle, fontnum, points, str, len, cords)
277
278function to get a strings bounding box given the font id and sizes
279
280 handle - pointer to font handle
281 fontnum - t1 library font id
282 points - number of points in fontheight
283 str - string to measure
284 len - string length
285 cords - the bounding box (modified in place)
286
287=cut
288*/
289
290void
291i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6]) {
292 BBox bbox;
293 BBox gbbox;
294
295 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
296 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
297 bbox = T1_GetStringBBox(fontnum,str,len,0,T1_KERNING);
298 gbbox = T1_GetFontBBox(fontnum);
299
300 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
301 (int)(bbox.llx*points/1000),
302 (int)(gbbox.lly*points/1000),
303 (int)(bbox.urx*points/1000),
304 (int)(gbbox.ury*points/1000),
305 (int)(bbox.lly*points/1000),
306 (int)(bbox.ury*points/1000) ));
307
308
309 cords[0]=((float)bbox.llx*points)/1000;
310 cords[2]=((float)bbox.urx*points)/1000;
311
312 cords[1]=((float)gbbox.lly*points)/1000;
313 cords[3]=((float)gbbox.ury*points)/1000;
314
315 cords[4]=((float)bbox.lly*points)/1000;
316 cords[5]=((float)bbox.ury*points)/1000;
317}
318
319
320/*
321=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
322
323Interface to text rendering in a single color onto an image
324
325 im - pointer to image structure
326 xb - x coordinate of start of string
327 yb - y coordinate of start of string ( see align )
328 cl - color to draw the text in
329 fontnum - t1 library font id
330 points - number of points in fontheight
331 str - char pointer to string to render
332 len - string length
333 align - (0 - top of font glyph | 1 - baseline )
334
335=cut
336*/
337
338undef_int
339i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str,int len,int align) {
340 GLYPH *glyph;
341 int xsize,ysize,x,y,ch;
342 i_color val;
343 unsigned char c,i;
344
345 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
346
347 glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
faa9b3e7
TC
348 if (glyph == NULL)
349 return 0;
02d1d628
AMH
350
351 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
352 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
353 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
354 mm_log((1,"bpp: %d\n",glyph->bpp));
355
356 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
357 ysize=glyph->metrics.ascent-glyph->metrics.descent;
358
359 mm_log((1,"width: %d height: %d\n",xsize,ysize));
360
361 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
362
363 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
364 c=glyph->bits[y*xsize+x];
365 i=255-c;
366 i_gpix(im,x+xb,y+yb,&val);
367 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
368 i_ppix(im,x+xb,y+yb,&val);
369 }
370 return 1;
371}
372
373
374#endif /* HAVE_LIBT1 */
375
376
4f68b48f 377/* Truetype font support */
02d1d628 378
4f68b48f 379#ifdef HAVE_LIBTT
02d1d628 380
4f68b48f
TC
381#include <freetype.h>
382#define TT_CHC 5
02d1d628 383
4f68b48f
TC
384/* convert a code point into an index in the glyph cache */
385#define TT_HASH(x) ((x) & 0xFF)
02d1d628 386
4f68b48f
TC
387typedef struct i_glyph_entry_ {
388 TT_Glyph glyph;
389 unsigned long ch;
390} i_tt_glyph_entry;
02d1d628 391
4f68b48f 392#define TT_NOCHAR (~0UL)
02d1d628 393
4f68b48f
TC
394struct TT_Instancehandle_ {
395 TT_Instance instance;
396 TT_Instance_Metrics imetrics;
397 TT_Glyph_Metrics gmetrics[256];
398 i_tt_glyph_entry glyphs[256];
399 int smooth;
400 int ptsize;
401 int order;
402};
02d1d628 403
4f68b48f 404typedef struct TT_Instancehandle_ TT_Instancehandle;
02d1d628 405
4f68b48f
TC
406struct TT_Fonthandle_ {
407 TT_Face face;
408 TT_Face_Properties properties;
409 TT_Instancehandle instanceh[TT_CHC];
410 TT_CharMap char_map;
411};
02d1d628
AMH
412
413/* Defines */
414
415#define USTRCT(x) ((x).z)
416#define TT_VALID( handle ) ( ( handle ).z != NULL )
417
418
419/* Prototypes */
420
421static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
422static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
423static void i_tt_done_raster_map( TT_Raster_Map *bit );
424static void i_tt_clear_raster_map( TT_Raster_Map* bit );
425static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
4f68b48f
TC
426static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
427static void
428i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
429 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
430 int x_off, int y_off, int smooth );
431static int
432i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
433 TT_Raster_Map *small_bit, int cords[6],
434 char const* txt, int len, int smooth, int utf8 );
02d1d628
AMH
435static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
436static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
4f68b48f
TC
437static int
438i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
439 float points, char const* txt, int len, int smooth, int utf8 );
440static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
02d1d628
AMH
441
442
443/* static globals needed */
444
445static TT_Engine engine;
446static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
447static int LTT_hinted = 1; /* FIXME: this too */
448
449
450/*
451 * FreeType interface
452 */
453
454
455/*
456=item init_tt()
457
458Initializes the freetype font rendering engine
459
460=cut
461*/
462
463undef_int
b33c08f8 464i_init_tt() {
02d1d628
AMH
465 TT_Error error;
466 mm_log((1,"init_tt()\n"));
467 error = TT_Init_FreeType( &engine );
468 if ( error ){
469 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
470 return(1);
471 }
472 return(0);
473}
474
475
476/*
477=item i_tt_get_instance(handle, points, smooth)
478
479Finds a points+smooth instance or if one doesn't exist in the cache
480allocates room and returns its cache entry
481
482 fontname - path to the font to load
483 handle - handle to the font.
484 points - points of the requested font
485 smooth - boolean (True: antialias on, False: antialias is off)
486
487=cut
488*/
489
490static
491int
492i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
493 int i,idx;
494 TT_Error error;
495
4f68b48f
TC
496 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
497 handle,points,smooth));
02d1d628
AMH
498
499 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
500 for(i=0;i<TT_CHC;i++) {
501 if (handle->instanceh[i].ptsize==points) {
502 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
503 return i;
504 }
02d1d628
AMH
505 }
506 smooth=1; /* We will be adding a font - add it as smooth then */
507 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
508 for(i=0;i<TT_CHC;i++) {
509 if (handle->instanceh[i].ptsize == points
510 && handle->instanceh[i].smooth == smooth) {
511 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
512 return i;
513 }
02d1d628
AMH
514 }
515 }
516
517 /* Found the instance in the cache - return the cache index */
518
4f68b48f
TC
519 for(idx=0;idx<TT_CHC;idx++) {
520 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
521 }
02d1d628
AMH
522
523 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
4f68b48f
TC
524 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
525 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
526
527 if ( USTRCT(handle->instanceh[idx].instance) ) {
528 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 529
4f68b48f 530 /* Free cached glyphs */
93d0372f 531 for(i=0;i<256;i++)
4f68b48f
TC
532 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
533 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 534
4f68b48f
TC
535 for(i=0;i<256;i++) {
536 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
537 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
538 }
539
540 /* Free instance if needed */
541 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
542 }
543
544 /* create and initialize instance */
545 /* FIXME: probably a memory leak on fail */
546
547 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
548 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
549 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
550
551 if ( error ) {
552 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
553 return -1;
554 }
555
556 /* Now that the instance should the inplace we need to lower all of the
557 ru counts and put `this' one with the highest entry */
558
559 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
560
561 handle->instanceh[idx].order=TT_CHC-1;
562 handle->instanceh[idx].ptsize=points;
563 handle->instanceh[idx].smooth=smooth;
564 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
565
4f68b48f
TC
566 /* Zero the memory for the glyph storage so they are not thought as
567 cached if they haven't been cached since this new font was loaded */
02d1d628 568
4f68b48f
TC
569 for(i=0;i<256;i++) {
570 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
571 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
572 }
02d1d628
AMH
573
574 return idx;
575}
576
577
578/*
579=item i_tt_new(fontname)
580
581Creates a new font handle object, finds a character map and initialise the
582the font handle's cache
583
584 fontname - path to the font to load
585
586=cut
587*/
588
589TT_Fonthandle*
590i_tt_new(char *fontname) {
591 TT_Error error;
592 TT_Fonthandle *handle;
593 unsigned short i,n;
594 unsigned short platform,encoding;
595
596 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
597
598 /* allocate memory for the structure */
599
4b19f77a 600 handle = mymalloc( sizeof(TT_Fonthandle) );
02d1d628
AMH
601
602 /* load the typeface */
603 error = TT_Open_Face( engine, fontname, &handle->face );
604 if ( error ) {
4f68b48f
TC
605 if ( error == TT_Err_Could_Not_Open_File ) {
606 mm_log((1, "Could not find/open %s.\n", fontname ));
607 }
608 else {
609 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
610 error ));
611 }
02d1d628
AMH
612 return NULL;
613 }
614
615 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 616
02d1d628 617 /* First, look for a Unicode charmap */
02d1d628
AMH
618 n = handle->properties.num_CharMaps;
619 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
620
621 for ( i = 0; i < n; i++ ) {
622 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
623 if ( (platform == 3 && encoding == 1 )
624 || (platform == 0 && encoding == 0 ) ) {
625 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
626 platform, encoding));
02d1d628
AMH
627 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
628 break;
629 }
630 }
27e79497
TC
631 if (!USTRCT(handle->char_map) && n != 0) {
632 /* just use the first one */
633 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
634 }
02d1d628
AMH
635
636 /* Zero the pointsizes - and ordering */
637
638 for(i=0;i<TT_CHC;i++) {
639 USTRCT(handle->instanceh[i].instance)=NULL;
640 handle->instanceh[i].order=i;
641 handle->instanceh[i].ptsize=0;
642 handle->instanceh[i].smooth=-1;
643 }
644
645 mm_log((1,"i_tt_new <- 0x%X\n",handle));
646 return handle;
647}
648
649
650
651/*
652