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