Commit | Line | Data |
---|---|---|
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 | ||
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, 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 | ||
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 4 | |
47 | ||
48 | =cut | |
49 | ||
50 | */ | |
51 | ||
52 | ||
53 | ||
54 | ||
55 | ||
56 | ||
57 | ||
58 | ||
59 | ||
60 | /* | |
61 | =item i_init_fonts() | |
62 | ||
63 | Initialize font rendering libraries if they are avaliable. | |
64 | ||
65 | =cut | |
66 | */ | |
67 | ||
68 | undef_int | |
69 | i_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 | ||
93 | Initializes the t1lib font rendering engine. | |
94 | ||
95 | =cut | |
96 | */ | |
97 | ||
98 | undef_int | |
99 | init_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 | ||
114 | Shuts the t1lib font rendering engine down. | |
115 | ||
116 | This it seems that this function is never used. | |
117 | ||
118 | =cut | |
119 | */ | |
120 | ||
121 | void | |
122 | i_close_t1() { | |
123 | T1_CloseLib(); | |
124 | } | |
125 | ||
126 | ||
127 | /* | |
128 | =item i_t1_new(pfb, afm) | |
129 | ||
130 | Loads 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 | ||
138 | int | |
139 | i_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 | ||
158 | Frees resources for a t1 font with given font id. | |
159 | ||
160 | font_id - number of the font to free | |
161 | ||
162 | =cut | |
163 | */ | |
164 | ||
165 | int | |
166 | i_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 | ||
175 | Sets the antialiasing level of the t1 library. | |
176 | ||
177 | st - 0 = NONE, 1 = LOW, 2 = HIGH. | |
178 | ||
179 | =cut | |
180 | */ | |
181 | ||
182 | void | |
183 | i_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 | ||
212 | Interface 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 | ||
227 | undef_int | |
228 | i_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 | ||
267 | function 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 | ||
279 | void | |
280 | i_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 | ||
312 | Interface 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 | ||
327 | undef_int | |
328 | i_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 | ||
385 | static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ); | |
386 | static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ); | |
387 | static void i_tt_done_raster_map( TT_Raster_Map *bit ); | |
388 | static void i_tt_clear_raster_map( TT_Raster_Map* bit ); | |
389 | static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ); | |
390 | static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j ); | |
391 | static 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 ); | |
392 | static 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 ); | |
393 | static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ); | |
394 | static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ); | |
395 | static int i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth ); | |
396 | static 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 | ||
401 | static TT_Engine engine; | |
402 | static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */ | |
403 | static int LTT_hinted = 1; /* FIXME: this too */ | |
404 | ||
405 | ||
406 | /* | |
407 | * FreeType interface | |
408 | */ | |
409 | ||
410 | ||
411 | /* | |
412 | =item init_tt() | |
413 | ||
414 | Initializes the freetype font rendering engine | |
415 | ||
416 | =cut | |
417 | */ | |
418 | ||
419 | undef_int | |
420 | init_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 | ||
435 | Finds a points+smooth instance or if one doesn't exist in the cache | |
436 | allocates 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 | ||
446 | static | |
447 | int | |
448 | i_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 | ||
513 | Creates a new font handle object, finds a character map and initialise the | |
514 | the font handle's cache | |
515 | ||
516 | fontname - path to the font to load | |
517 | ||
518 | =cut | |
519 | */ | |
520 | ||
521 | TT_Fonthandle* | |
522 | i_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 |