look at the correct %Config library and include paths
[imager.git] / font.c
CommitLineData
92bda632 1#include "imager.h"
fa16b6c6 2#include "imrender.h"
02d1d628
AMH
3
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7
8#include <stdio.h>
9#include <stdlib.h>
10
f75c1aeb
TC
11#ifdef HAVE_LIBT1
12#include <t1lib.h>
13#endif
14
15
02d1d628
AMH
16/*
17=head1 NAME
18
19font.c - implements font handling functions for t1 and truetype fonts
20
21=head1 SYNOPSIS
22
23 i_init_fonts();
24
25 #ifdef HAVE_LIBT1
26 fontnum = i_t1_new(path_to_pfb, path_to_afm);
27 i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
28 rc = i_t1_destroy(fontnum);
29 #endif
30
31 #ifdef HAVE_LIBTT
32 handle = i_tt_new(path_to_ttf);
eeaa33fd 33 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
02d1d628
AMH
34 i_tt_destroy(handle);
35
36 // and much more
37
38=head1 DESCRIPTION
39
40font.c implements font creation, rendering, bounding box functions and
41more for Imager.
42
43=head1 FUNCTION REFERENCE
44
45Some of these functions are internal.
46
b8c2033e 47=over
02d1d628
AMH
48
49=cut
50
51*/
52
02d1d628
AMH
53/*
54=item i_init_fonts()
55
56Initialize font rendering libraries if they are avaliable.
57
58=cut
59*/
60
61undef_int
4cb58f1b 62i_init_fonts(int t1log) {
02d1d628
AMH
63 mm_log((1,"Initializing fonts\n"));
64
65#ifdef HAVE_LIBT1
d1555273 66 if (i_init_t1(t1log))
faa9b3e7
TC
67 return 0;
68#endif
d1555273 69
02d1d628
AMH
70 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
71}
72
73
74
75
76#ifdef HAVE_LIBT1
77
1bd75e4c
TC
78static int t1_get_flags(char const *flags);
79static char *t1_from_utf8(char const *in, int len, int *outlen);
02d1d628 80
3799c4d1
TC
81static void t1_push_error(void);
82
3a12a980
TC
83static int t1_active_fonts = 0;
84static int t1_initialized = 0;
85
02d1d628 86/*
4cb58f1b 87=item i_init_t1(t1log)
02d1d628
AMH
88
89Initializes the t1lib font rendering engine.
90
91=cut
92*/
93
94undef_int
b33c08f8 95i_init_t1(int t1log) {
4cb58f1b 96 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
02d1d628 97 mm_log((1,"init_t1()\n"));
3a12a980 98
d1555273
TC
99 i_clear_error();
100
3a12a980
TC
101 if (t1_active_fonts) {
102 mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
d1555273 103 i_push_error(0, "Cannot re-initialize T1 - active fonts");
3a12a980
TC
104 return 1;
105 }
106
107 if (t1_initialized) {
108 T1_CloseLib();
109 }
4cb58f1b
TC
110
111 if (t1log)
112 init_flags |= LOGFILE;
113 if ((T1_InitLib(init_flags) == NULL)){
02d1d628 114 mm_log((1,"Initialization of t1lib failed\n"));
d1555273 115 i_push_error(0, "T1_InitLib failed");
02d1d628
AMH
116 return(1);
117 }
118 T1_SetLogLevel(T1LOG_DEBUG);
119 i_t1_set_aa(1); /* Default Antialias value */
3a12a980
TC
120
121 ++t1_initialized;
122
02d1d628
AMH
123 return(0);
124}
125
126
127/*
128=item i_close_t1()
129
130Shuts the t1lib font rendering engine down.
131
132 This it seems that this function is never used.
133
134=cut
135*/
136
137void
faa9b3e7 138i_close_t1(void) {
02d1d628 139 T1_CloseLib();
3a12a980 140 t1_initialized = 0;
02d1d628
AMH
141}
142
143
144/*
145=item i_t1_new(pfb, afm)
146
147Loads the fonts with the given filenames, returns its font id
148
149 pfb - path to pfb file for font
150 afm - path to afm file for font
151
152=cut
153*/
154
155int
156i_t1_new(char *pfb,char *afm) {
157 int font_id;
1bd75e4c 158
d1555273
TC
159 i_clear_error();
160
161 if (!t1_initialized && i_init_t1(0))
162 return -1;
163
02d1d628
AMH
164 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
165 font_id = T1_AddFont(pfb);
166 if (font_id<0) {
167 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
168 return font_id;
169 }
170
171 if (afm != NULL) {
172 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
173 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
174 }
1bd75e4c 175
3a12a980
TC
176 ++t1_active_fonts;
177
02d1d628
AMH
178 return font_id;
179}
180
181/*
182=item i_t1_destroy(font_id)
183
184Frees resources for a t1 font with given font id.
185
186 font_id - number of the font to free
187
188=cut
189*/
190
191int
192i_t1_destroy(int font_id) {
193 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
3a12a980
TC
194
195 --t1_active_fonts;
196
02d1d628
AMH
197 return T1_DeleteFont(font_id);
198}
199
200
201/*
202=item i_t1_set_aa(st)
203
204Sets the antialiasing level of the t1 library.
205
206 st - 0 = NONE, 1 = LOW, 2 = HIGH.
207
208=cut
209*/
210
211void
212i_t1_set_aa(int st) {
213 int i;
214 unsigned long cst[17];
215 switch(st) {
216 case 0:
217 T1_AASetBitsPerPixel( 8 );
218 T1_AASetLevel( T1_AA_NONE );
219 T1_AANSetGrayValues( 0, 255 );
220 mm_log((1,"setting T1 antialias to none\n"));
221 break;
222 case 1:
223 T1_AASetBitsPerPixel( 8 );
224 T1_AASetLevel( T1_AA_LOW );
225 T1_AASetGrayValues( 0,65,127,191,255 );
226 mm_log((1,"setting T1 antialias to low\n"));
227 break;
228 case 2:
229 T1_AASetBitsPerPixel(8);
230 T1_AASetLevel(T1_AA_HIGH);
231 for(i=0;i<17;i++) cst[i]=(i*255)/16;
232 T1_AAHSetGrayValues( cst );
233 mm_log((1,"setting T1 antialias to high\n"));
234 }
235}
236
237
238/*
239=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
240
241Interface to text rendering into a single channel in an image
242
243 im pointer to image structure
244 xb x coordinate of start of string
245 yb y coordinate of start of string ( see align )
246 channel - destination channel
247 fontnum - t1 library font id
248 points - number of points in fontheight
249 str - string to render
250 len - string length
251 align - (0 - top of font glyph | 1 - baseline )
252
253=cut
254*/
255
256undef_int
1bd75e4c 257i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) {
02d1d628
AMH
258 GLYPH *glyph;
259 int xsize,ysize,x,y;
260 i_color val;
1bd75e4c 261 int mod_flags = t1_get_flags(flags);
02d1d628
AMH
262
263 unsigned int ch_mask_store;
264
265 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
266
1bd75e4c
TC
267 if (utf8) {
268 int worklen;
269 char *work = t1_from_utf8(str, len, &worklen);
270 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
271 myfree(work);
272 }
273 else {
274 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
275 }
faa9b3e7
TC
276 if (glyph == NULL)
277 return 0;
02d1d628
AMH
278
279 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
280 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
281 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
282 mm_log((1,"bpp: %d\n",glyph->bpp));
283
284 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
285 ysize=glyph->metrics.ascent-glyph->metrics.descent;
286
287 mm_log((1,"width: %d height: %d\n",xsize,ysize));
288
289 ch_mask_store=im->ch_mask;
290 im->ch_mask=1<<channel;
291
292 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
293
294 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
295 val.channel[channel]=glyph->bits[y*xsize+x];
296 i_ppix(im,x+xb,y+yb,&val);
297 }
298
299 im->ch_mask=ch_mask_store;
300 return 1;
301}
302
5eebac42
TC
303static void
304t1_fix_bbox(BBox *bbox, const char *str, int len, int advance,
305 int space_position) {
306 /* never called with len == 0 */
307 if (str[0] == space_position && bbox->llx > 0)
308 bbox->llx = 0;
309 if (str[len-1] == space_position && bbox->urx < advance)
310 bbox->urx = advance;
311 if (bbox->lly > bbox->ury)
312 bbox->lly = bbox->ury = 0;
313}
02d1d628
AMH
314
315/*
316=item i_t1_bbox(handle, fontnum, points, str, len, cords)
317
318function to get a strings bounding box given the font id and sizes
319
320 handle - pointer to font handle
321 fontnum - t1 library font id
322 points - number of points in fontheight
323 str - string to measure
324 len - string length
325 cords - the bounding box (modified in place)
326
327=cut
328*/
329
3799c4d1 330int
97ac0a96 331i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) {
02d1d628
AMH
332 BBox bbox;
333 BBox gbbox;
1bd75e4c 334 int mod_flags = t1_get_flags(flags);
3799c4d1 335 int advance;
5eebac42 336 int space_position = T1_GetEncodingIndex(fontnum, "space");
02d1d628
AMH
337
338 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
339 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
5eebac42
TC
340
341 if (len == 0) {
342 /* len == 0 has special meaning to T1lib, but it means there's
343 nothing to draw, so return that */
344 bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
345 advance = 0;
1bd75e4c
TC
346 }
347 else {
5eebac42
TC
348 if (utf8) {
349 int worklen;
350 char *work = t1_from_utf8(str, len, &worklen);
c1b3b04b 351 advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
5eebac42
TC
352 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
353 t1_fix_bbox(&bbox, work, worklen, advance, space_position);
354 myfree(work);
355 }
356 else {
c1b3b04b 357 advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
5eebac42
TC
358 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
359 t1_fix_bbox(&bbox, str, len, advance, space_position);
360 }
1bd75e4c 361 }
02d1d628
AMH
362 gbbox = T1_GetFontBBox(fontnum);
363
364 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
365 (int)(bbox.llx*points/1000),
366 (int)(gbbox.lly*points/1000),
367 (int)(bbox.urx*points/1000),
368 (int)(gbbox.ury*points/1000),
369 (int)(bbox.lly*points/1000),
370 (int)(bbox.ury*points/1000) ));
371
372
3799c4d1
TC
373 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
374 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
375
376 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
377 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
378
379 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
380 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
02d1d628 381
3799c4d1 382 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
7fdbfba8
TC
383 cords[BBOX_RIGHT_BEARING] =
384 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
02d1d628 385
7fdbfba8 386 return BBOX_RIGHT_BEARING+1;
02d1d628
AMH
387}
388
389
390/*
391=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
392
393Interface to text rendering in a single color onto an image
394
395 im - pointer to image structure
396 xb - x coordinate of start of string
397 yb - y coordinate of start of string ( see align )
398 cl - color to draw the text in
399 fontnum - t1 library font id
400 points - number of points in fontheight
401 str - char pointer to string to render
402 len - string length
403 align - (0 - top of font glyph | 1 - baseline )
404
405=cut
406*/
407
408undef_int
97ac0a96 409i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,int len,int align, int utf8, char const *flags) {
02d1d628 410 GLYPH *glyph;
919e0000 411 int xsize,ysize,y;
1bd75e4c 412 int mod_flags = t1_get_flags(flags);
4c84ccfb 413 i_render r;
02d1d628
AMH
414
415 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
416
1bd75e4c
TC
417 if (utf8) {
418 int worklen;
419 char *work = t1_from_utf8(str, len, &worklen);
420 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
421 myfree(work);
422 }
423 else {
97ac0a96
TC
424 /* T1_AASetString() accepts a char * not a const char */
425 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
1bd75e4c 426 }
faa9b3e7
TC
427 if (glyph == NULL)
428 return 0;
02d1d628
AMH
429
430 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
431 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
432 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
433 mm_log((1,"bpp: %d\n",glyph->bpp));
434
435 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
436 ysize=glyph->metrics.ascent-glyph->metrics.descent;
437
438 mm_log((1,"width: %d height: %d\n",xsize,ysize));
439
440 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
4c84ccfb
TC
441
442 i_render_init(&r, im, xsize);
443 for(y=0;y<ysize;y++) {
919e0000 444 i_render_color(&r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
02d1d628 445 }
4c84ccfb
TC
446 i_render_done(&r);
447
02d1d628
AMH
448 return 1;
449}
450
1bd75e4c
TC
451/*
452=item t1_get_flags(flags)
453
454Processes the characters in I<flags> to create a mod_flags value used
455by some T1Lib functions.
456
457=cut
458 */
459static int
460t1_get_flags(char const *flags) {
461 int mod_flags = T1_KERNING;
462
463 while (*flags) {
464 switch (*flags++) {
465 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
466 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
467 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
468 /* ignore anything we don't recognize */
469 }
470 }
471
472 return mod_flags;
473}
474
475/*
476=item t1_from_utf8(char const *in, int len, int *outlen)
477
478Produces an unencoded version of I<in> by dropping any Unicode
479character over 255.
480
481Returns a newly allocated buffer which should be freed with myfree().
482Sets *outlen to the number of bytes used in the output string.
483
484=cut
485*/
486
487static char *
488t1_from_utf8(char const *in, int len, int *outlen) {
f0960b14
TC
489 /* at this point len is from a perl SV, so can't approach MAXINT */
490 char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
1bd75e4c
TC
491 char *p = out;
492 unsigned long c;
493
494 while (len) {
495 c = i_utf8_advance(&in, &len);
496 if (c == ~0UL) {
497 myfree(out);
498 i_push_error(0, "invalid UTF8 character");
499 return 0;
500 }
501 /* yeah, just drop them */
502 if (c < 0x100) {
503 *p++ = (char)c;
504 }
505 }
506 *p = '\0';
507 *outlen = p - out;
508
509 return out;
510}
02d1d628 511
3799c4d1
TC
512/*
513=item i_t1_has_chars(font_num, text, len, utf8, out)
514
515Check if the given characters are defined by the font. Note that len
516is the number of bytes, not the number of characters (when utf8 is
517non-zero).
518
519out[char index] will be true if the character exists.
520
521Accepts UTF-8, but since T1 can only have 256 characters, any chars
522with values over 255 will simply be returned as false.
523
524Returns the number of characters that were checked.
525
526=cut
527*/
528
529int
530i_t1_has_chars(int font_num, const char *text, int len, int utf8,
531 char *out) {
532 int count = 0;
533
534 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
535 font_num, text, len, utf8));
536
537 i_clear_error();
538 if (T1_LoadFont(font_num)) {
539 t1_push_error();
540 return 0;
541 }
542
543 while (len) {
544 unsigned long c;
3799c4d1
TC
545 if (utf8) {
546 c = i_utf8_advance(&text, &len);
547 if (c == ~0UL) {
548 i_push_error(0, "invalid UTF8 character");
549 return 0;
550 }
551 }
552 else {
553 c = (unsigned char)*text++;
554 --len;
555 }
556
557 if (c >= 0x100) {
558 /* limit of 256 characters for T1 */
559 *out++ = 0;
560 }
561 else {
562 char const * name = T1_GetCharName(font_num, (unsigned char)c);
563
564 if (name) {
565 *out++ = strcmp(name, ".notdef") != 0;
566 }
567 else {
568 mm_log((2, " No name found for character %lx\n", c));
569 *out++ = 0;
570 }
571 }
572 ++count;
573 }
574
575 return count;
576}
577
578/*
579=item i_t1_face_name(font_num, name_buf, name_buf_size)
580
581Copies the face name of the given C<font_num> to C<name_buf>. Returns
582the number of characters required to store the name (which can be
583larger than C<name_buf_size>, including the space required to store
584the terminating NUL).
585
586If name_buf is too small (as specified by name_buf_size) then the name
587will be truncated. name_buf will always be NUL termintaed.
588
589=cut
590*/
591
592int
593i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
594 char *name;
595
596 T1_errno = 0;
597 if (T1_LoadFont(font_num)) {
598 t1_push_error();
599 return 0;
600 }
601 name = T1_GetFontName(font_num);
602
603 if (name) {
604 strncpy(name_buf, name, name_buf_size);
605 name_buf[name_buf_size-1] = '\0';
606 return strlen(name) + 1;
607 }
608 else {
609 t1_push_error();
610 return 0;
611 }
612}
613
614int
615i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
616 size_t name_buf_size) {
617 char *name;
618
619 i_clear_error();
620 if (ch > 0xFF) {
621 return 0;
622 }
623 if (T1_LoadFont(font_num)) {
624 t1_push_error();
625 return 0;
626 }
627 name = T1_GetCharName(font_num, (unsigned char)ch);
628 if (name) {
629 if (strcmp(name, ".notdef")) {
630 strncpy(name_buf, name, name_buf_size);
631 name_buf[name_buf_size-1] = '\0';
632 return strlen(name) + 1;
633 }
634 else {
635 return 0;
636 }
637 }
638 else {
639 t1_push_error();
640 return 0;
641 }
642}
643
644static void
645t1_push_error(void) {
646 switch (T1_errno) {
647 case 0:
648 i_push_error(0, "No error");
649 break;
650
d5a3f460 651#ifdef T1ERR_SCAN_FONT_FORMAT
3799c4d1
TC
652 case T1ERR_SCAN_FONT_FORMAT:
653 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
654 break;
d5a3f460 655#endif
3799c4d1 656
d5a3f460 657#ifdef T1ERR_SCAN_FILE_OPEN_ERR
3799c4d1
TC
658 case T1ERR_SCAN_FILE_OPEN_ERR:
659 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
660 break;
d5a3f460 661#endif
3799c4d1 662
d5a3f460 663#ifdef T1ERR_SCAN_OUT_OF_MEMORY
3799c4d1
TC
664 case T1ERR_SCAN_OUT_OF_MEMORY:
665 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
666 break;
d5a3f460 667#endif
3799c4d1 668
d5a3f460 669#ifdef T1ERR_SCAN_ERROR
3799c4d1
TC
670 case T1ERR_SCAN_ERROR:
671 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
672 break;
d5a3f460 673#endif
3799c4d1 674
d5a3f460 675#ifdef T1ERR_SCAN_FILE_EOF
3799c4d1
TC
676 case T1ERR_SCAN_FILE_EOF:
677 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
678 break;
d5a3f460 679#endif
3799c4d1 680
d5a3f460 681#ifdef T1ERR_PATH_ERROR
3799c4d1
TC
682 case T1ERR_PATH_ERROR:
683 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
684 break;
d5a3f460 685#endif
3799c4d1 686
d5a3f460 687#ifdef T1ERR_PARSE_ERROR
3799c4d1
TC
688 case T1ERR_PARSE_ERROR:
689 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
690 break;
d5a3f460 691#endif
3799c4d1 692
d5a3f460 693#ifdef T1ERR_TYPE1_ABORT
3799c4d1
TC
694 case T1ERR_TYPE1_ABORT:
695 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
696 break;
d5a3f460 697#endif
3799c4d1 698
d5a3f460 699#ifdef T1ERR_INVALID_FONTID
3799c4d1
TC
700 case T1ERR_INVALID_FONTID:
701 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
702 break;
d5a3f460 703#endif
3799c4d1 704
d5a3f460 705#ifdef T1ERR_INVALID_PARAMETER
3799c4d1
TC
706 case T1ERR_INVALID_PARAMETER:
707 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
708 break;
d5a3f460 709#endif
3799c4d1 710
d5a3f460 711#ifdef T1ERR_OP_NOT_PERMITTED
3799c4d1
TC
712 case T1ERR_OP_NOT_PERMITTED:
713 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
714 break;
d5a3f460 715#endif
3799c4d1 716
d5a3f460 717#ifdef T1ERR_ALLOC_MEM
3799c4d1
TC
718 case T1ERR_ALLOC_MEM:
719 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
720 break;
d5a3f460 721#endif
3799c4d1 722
d5a3f460 723#ifdef T1ERR_FILE_OPEN_ERR
3799c4d1
TC
724 case T1ERR_FILE_OPEN_ERR:
725 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
726 break;
d5a3f460 727#endif
3799c4d1 728
d5a3f460 729#ifdef T1ERR_UNSPECIFIED
3799c4d1
TC
730 case T1ERR_UNSPECIFIED:
731 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
732 break;
d5a3f460 733#endif
3799c4d1 734
d5a3f460 735#ifdef T1ERR_NO_AFM_DATA
3799c4d1
TC
736 case T1ERR_NO_AFM_DATA:
737 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
738 break;
d5a3f460 739#endif
3799c4d1 740
d5a3f460 741#ifdef T1ERR_X11
3799c4d1
TC
742 case T1ERR_X11:
743 i_push_error(T1ERR_X11, "X11");
744 break;
d5a3f460 745#endif
3799c4d1 746
d5a3f460 747#ifdef T1ERR_COMPOSITE_CHAR
3799c4d1
TC
748 case T1ERR_COMPOSITE_CHAR:
749 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
750 break;
d5a3f460 751#endif
3799c4d1
TC
752
753 default:
754 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
755 }
756}
757
02d1d628
AMH
758#endif /* HAVE_LIBT1 */
759
760
4f68b48f 761/* Truetype font support */
4f68b48f 762#ifdef HAVE_LIBTT
02d1d628 763
d93d5c10 764/* These are enabled by default when configuring Freetype 1.x
3799c4d1
TC
765 I haven't a clue how to reliably detect it at compile time.
766
767 We need a compilation probe in Makefile.PL
768*/
769#define FTXPOST 1
d93d5c10 770#define FTXERR18 1
3799c4d1 771
4f68b48f
TC
772#include <freetype.h>
773#define TT_CHC 5
02d1d628 774
3799c4d1
TC
775#ifdef FTXPOST
776#include <ftxpost.h>
777#endif
778
d93d5c10
TC
779#ifdef FTXERR18
780#include <ftxerr18.h>
781#endif
782
a90b253d
TC
783/* some versions of FT1.x don't seem to define this - it's font defined
784 so it won't change */
785#ifndef TT_MS_LANGID_ENGLISH_GENERAL
786#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
787#endif
788
4f68b48f
TC
789/* convert a code point into an index in the glyph cache */
790#define TT_HASH(x) ((x) & 0xFF)
02d1d628 791
4f68b48f
TC
792typedef struct i_glyph_entry_ {
793 TT_Glyph glyph;
794 unsigned long ch;
795} i_tt_glyph_entry;
02d1d628 796
4f68b48f 797#define TT_NOCHAR (~0UL)
02d1d628 798
4f68b48f
TC
799struct TT_Instancehandle_ {
800 TT_Instance instance;
801 TT_Instance_Metrics imetrics;
802 TT_Glyph_Metrics gmetrics[256];
803 i_tt_glyph_entry glyphs[256];
804 int smooth;
805 int ptsize;
806 int order;
807};
02d1d628 808
4f68b48f 809typedef struct TT_Instancehandle_ TT_Instancehandle;
02d1d628 810
4f68b48f
TC
811struct TT_Fonthandle_ {
812 TT_Face face;
813 TT_Face_Properties properties;
814 TT_Instancehandle instanceh[TT_CHC];
815 TT_CharMap char_map;
3799c4d1
TC
816#ifdef FTXPOST
817 int loaded_names;
818 TT_Error load_cond;
819#endif
4f68b48f 820};
02d1d628
AMH
821
822/* Defines */
823
824#define USTRCT(x) ((x).z)
825#define TT_VALID( handle ) ( ( handle ).z != NULL )
826
d93d5c10 827static void i_tt_push_error(TT_Error rc);
02d1d628
AMH
828
829/* Prototypes */
830
831static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
832static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
833static void i_tt_done_raster_map( TT_Raster_Map *bit );
834static void i_tt_clear_raster_map( TT_Raster_Map* bit );
835static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
4f68b48f
TC
836static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
837static void
838i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
839 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
840 int x_off, int y_off, int smooth );
841static int
842i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
843 TT_Raster_Map *small_bit, int cords[6],
844 char const* txt, int len, int smooth, int utf8 );
97ac0a96 845static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
02d1d628 846static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
4f68b48f
TC
847static int
848i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
849 float points, char const* txt, int len, int smooth, int utf8 );
850static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
02d1d628
AMH
851
852
853/* static globals needed */
854
d1555273 855static int TT_initialized = 0;
02d1d628
AMH
856static TT_Engine engine;
857static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
858static int LTT_hinted = 1; /* FIXME: this too */
859
860
861/*
862 * FreeType interface
863 */
864
865
866/*
867=item init_tt()
868
869Initializes the freetype font rendering engine
870
871=cut
872*/
873
874undef_int
d1555273 875i_init_tt(void) {
02d1d628 876 TT_Error error;
95b2bff4
TC
877 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
878
d1555273
TC
879 i_clear_error();
880
02d1d628
AMH
881 mm_log((1,"init_tt()\n"));
882 error = TT_Init_FreeType( &engine );
883 if ( error ){
884 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
d1555273
TC
885 i_tt_push_error(error);
886 i_push_error(0, "Could not initialize freetype 1.x");
02d1d628
AMH
887 return(1);
888 }
3799c4d1
TC
889
890#ifdef FTXPOST
891 error = TT_Init_Post_Extension( engine );
892 if (error) {
893 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
d1555273
TC
894
895 i_tt_push_error(error);
896 i_push_error(0, "Could not initialize FT 1.x POST extension");
3799c4d1
TC
897 return 1;
898 }
899#endif
900
95b2bff4
TC
901 error = TT_Set_Raster_Gray_Palette(engine, palette);
902 if (error) {
903 mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
d1555273
TC
904 i_tt_push_error(error);
905 i_push_error(0, "Could not initialize FT 1.x POST extension");
95b2bff4
TC
906 return 1;
907 }
908
d1555273
TC
909 TT_initialized = 1;
910
02d1d628
AMH
911 return(0);
912}
913
914
915/*
916=item i_tt_get_instance(handle, points, smooth)
917
918Finds a points+smooth instance or if one doesn't exist in the cache
919allocates room and returns its cache entry
920
921 fontname - path to the font to load
922 handle - handle to the font.
923 points - points of the requested font
924 smooth - boolean (True: antialias on, False: antialias is off)
925
926=cut
927*/
928
929static
930int
931i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
932 int i,idx;
933 TT_Error error;
934
4f68b48f
TC
935 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
936 handle,points,smooth));
02d1d628
AMH
937
938 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
939 for(i=0;i<TT_CHC;i++) {
940 if (handle->instanceh[i].ptsize==points) {
941 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
942 return i;
943 }
02d1d628
AMH
944 }
945 smooth=1; /* We will be adding a font - add it as smooth then */
946 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
947 for(i=0;i<TT_CHC;i++) {
948 if (handle->instanceh[i].ptsize == points
949 && handle->instanceh[i].smooth == smooth) {
950 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
951 return i;
952 }
02d1d628
AMH
953 }
954 }
955
956 /* Found the instance in the cache - return the cache index */
957
4f68b48f
TC
958 for(idx=0;idx<TT_CHC;idx++) {
959 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
960 }
02d1d628
AMH
961
962 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
4f68b48f
TC
963 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
964 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
965
966 if ( USTRCT(handle->instanceh[idx].instance) ) {
967 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 968
4f68b48f 969 /* Free cached glyphs */
93d0372f 970 for(i=0;i<256;i++)
4f68b48f
TC
971 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
972 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 973
4f68b48f
TC
974 for(i=0;i<256;i++) {
975 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
976 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
977 }
978
979 /* Free instance if needed */
980 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
981 }
982
983 /* create and initialize instance */
984 /* FIXME: probably a memory leak on fail */
985
986 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
987 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
988 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
989
990 if ( error ) {
991 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
992 return -1;
993 }
994
995 /* Now that the instance should the inplace we need to lower all of the
996 ru counts and put `this' one with the highest entry */
997
998 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
999
1000 handle->instanceh[idx].order=TT_CHC-1;
1001 handle->instanceh[idx].ptsize=points;
1002 handle->instanceh[idx].smooth=smooth;
1003 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
1004
4f68b48f
TC
1005 /* Zero the memory for the glyph storage so they are not thought as
1006 cached if they haven't been cached since this new font was loaded */
02d1d628 1007
4f68b48f
TC
1008 for(i=0;i<256;i++) {
1009 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
1010 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
1011 }
02d1d628
AMH
1012
1013 return idx;
1014}
1015
1016
1017/*
1018=item i_tt_new(fontname)
1019
1020Creates a new font handle object, finds a character map and initialise the
1021the font handle's cache
1022
1023 fontname - path to the font to load
1024
1025=cut
1026*/
1027
1028TT_Fonthandle*
97ac0a96 1029i_tt_new(const char *fontname) {
02d1d628
AMH
1030 TT_Error error;
1031 TT_Fonthandle *handle;
1032 unsigned short i,n;
1033 unsigned short platform,encoding;
d93d5c10 1034
d1555273
TC
1035 if (!TT_initialized && i_init_tt()) {
1036 i_push_error(0, "Could not initialize FT1 engine");
1037 return NULL;
1038 }
1039
d93d5c10 1040 i_clear_error();
02d1d628
AMH
1041
1042 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1043
1044 /* allocate memory for the structure */
1045
f0960b14 1046 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
02d1d628
AMH
1047
1048 /* load the typeface */
1049 error = TT_Open_Face( engine, fontname, &handle->face );
1050 if ( error ) {
4f68b48f
TC
1051 if ( error == TT_Err_Could_Not_Open_File ) {
1052 mm_log((1, "Could not find/open %s.\n", fontname ));
1053 }
1054 else {
1055 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1056 error ));
1057 }
d93d5c10 1058 i_tt_push_error(error);
02d1d628
AMH
1059 return NULL;
1060 }
1061
1062 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 1063
02d1d628 1064 /* First, look for a Unicode charmap */
02d1d628
AMH
1065 n = handle->properties.num_CharMaps;
1066 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1067
1068 for ( i = 0; i < n; i++ ) {
1069 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
1070 if ( (platform == 3 && encoding == 1 )
1071 || (platform == 0 && encoding == 0 ) ) {
1072 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1073 platform, encoding));
02d1d628
AMH
1074 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1075 break;
1076 }
1077 }
27e79497
TC
1078 if (!USTRCT(handle->char_map) && n != 0) {
1079 /* just use the first one */
1080 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1081 }
02d1d628
AMH
1082
1083 /* Zero the pointsizes - and ordering */
1084
1085 for(i=0;i<TT_CHC;i++) {
1086 USTRCT(handle->instanceh[i].instance)=NULL;
1087 handle->instanceh[i].order=i;
1088 handle->instanceh[i].ptsize=0;
1089 handle->instanceh[i].smooth=-1;
1090 }
1091
3799c4d1
TC
1092#ifdef FTXPOST
1093 handle->loaded_names = 0;
1094#endif
1095
02d1d628
AMH
1096 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1097 return handle;
1098}
1099
1100
1101
1102/*
1103