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