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