- document the values for the read() and write() method type
[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
377
378
379
380
381
382
383
384
385/* Truetype font support */
386
387#ifdef HAVE_LIBTT
388
389
390/* Defines */
391
392#define USTRCT(x) ((x).z)
393#define TT_VALID( handle ) ( ( handle ).z != NULL )
394
395
396/* Prototypes */
397
398static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
399static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
400static void i_tt_done_raster_map( TT_Raster_Map *bit );
401static void i_tt_clear_raster_map( TT_Raster_Map* bit );
402static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
403static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j );
404static 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 );
405static 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 );
406static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
407static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
408static int i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth );
409static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] );
410
411
412/* static globals needed */
413
414static TT_Engine engine;
415static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
416static int LTT_hinted = 1; /* FIXME: this too */
417
418
419/*
420 * FreeType interface
421 */
422
423
424/*
425=item init_tt()
426
427Initializes the freetype font rendering engine
428
429=cut
430*/
431
432undef_int
b33c08f8 433i_init_tt() {
02d1d628
AMH
434 TT_Error error;
435 mm_log((1,"init_tt()\n"));
436 error = TT_Init_FreeType( &engine );
437 if ( error ){
438 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
439 return(1);
440 }
441 return(0);
442}
443
444
445/*
446=item i_tt_get_instance(handle, points, smooth)
447
448Finds a points+smooth instance or if one doesn't exist in the cache
449allocates room and returns its cache entry
450
451 fontname - path to the font to load
452 handle - handle to the font.
453 points - points of the requested font
454 smooth - boolean (True: antialias on, False: antialias is off)
455
456=cut
457*/
458
459static
460int
461i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
462 int i,idx;
463 TT_Error error;
464
465 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",handle,points,smooth));
466
467 if (smooth == -1) { /* Smooth doesn't matter for this search */
468 for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points) {
469 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
470 return i;
471 }
472 smooth=1; /* We will be adding a font - add it as smooth then */
473 } else { /* Smooth doesn't matter for this search */
474 for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points && handle->instanceh[i].smooth==smooth) {
475 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
476 return i;
477 }
478 }
479
480 /* Found the instance in the cache - return the cache index */
481
482 for(idx=0;idx<TT_CHC;idx++) if (!(handle->instanceh[idx].order)) break; /* find the lru item */
483
484 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
485 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",USTRCT(handle->instanceh[idx].instance) ));
486
487 if ( USTRCT(handle->instanceh[idx].instance) ) {
488 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f
AMH
489 /* Free cached glyphs */
490
491 for(i=0;i<256;i++)
492 if ( USTRCT(handle->instanceh[idx].glyphs[i]) )
493 TT_Done_Glyph( handle->instanceh[idx].glyphs[i] );
494
495 for(i=0;i<256;i++) USTRCT(handle->instanceh[idx].glyphs[i])=NULL;
02d1d628 496 TT_Done_Instance( handle->instanceh[idx].instance ); /* Free instance if needed */
93d0372f 497
02d1d628
AMH
498 }
499
500 /* create and initialize instance */
501 /* FIXME: probably a memory leak on fail */
502
503 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
504 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
505 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
506
507 if ( error ) {
508 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
509 return -1;
510 }
511
512 /* Now that the instance should the inplace we need to lower all of the
513 ru counts and put `this' one with the highest entry */
514
515 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
516
517 handle->instanceh[idx].order=TT_CHC-1;
518 handle->instanceh[idx].ptsize=points;
519 handle->instanceh[idx].smooth=smooth;
520 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
521
522 /* Zero the memory for the glyph storage so they are not thought as cached if they haven't been cached
523 since this new font was loaded */
524
525 for(i=0;i<256;i++) USTRCT(handle->instanceh[idx].glyphs[i])=NULL;
526
527 return idx;
528}
529
530
531/*
532=item i_tt_new(fontname)
533
534Creates a new font handle object, finds a character map and initialise the
535the font handle's cache
536
537 fontname - path to the font to load
538
539=cut
540*/
541
542TT_Fonthandle*
543i_tt_new(char *fontname) {
544 TT_Error error;
545 TT_Fonthandle *handle;
546 unsigned short i,n;
547 unsigned short platform,encoding;
548
549 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
550
551 /* allocate memory for the structure */
552
4b19f77a 553 handle = mymalloc( sizeof(TT_Fonthandle) );
02d1d628
AMH
554
555 /* load the typeface */
556 error = TT_Open_Face( engine, fontname, &handle->face );
557 if ( error ) {
cd9f9ddc
AMH
558 if ( error == TT_Err_Could_Not_Open_File ) { mm_log((1, "Could not find/open %s.\n", fontname )) }
559 else { mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, error )); }
02d1d628
AMH
560 return NULL;
561 }
562
563 TT_Get_Face_Properties( handle->face, &(handle->properties) );
564 /* First, look for a Unicode charmap */
565
566 n = handle->properties.num_CharMaps;
567 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
568
569 for ( i = 0; i < n; i++ ) {
570 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
571 if ( (platform == 3 && encoding == 1 ) || (platform == 0 && encoding == 0 ) ) {
27e79497 572 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", platform, encoding));
02d1d628
AMH
573 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
574 break;
575 }
576 }
27e79497
TC
577 if (!USTRCT(handle->char_map) && n != 0) {
578 /* just use the first one */
579 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
580 }
02d1d628
AMH
581
582 /* Zero the pointsizes - and ordering */
583
584 for(i=0;i<TT_CHC;i++) {
585 USTRCT(handle->instanceh[i].instance)=NULL;
586 handle->instanceh[i].order=i;
587 handle->instanceh[i].ptsize=0;
588 handle->instanceh[i].smooth=-1;
589 }
590
591 mm_log((1,"i_tt_new <- 0x%X\n",handle));
592 return handle;
593}
594
595
596
597/*
598