remove some dev code that was writing an extra image file
[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
5eebac42
TC
301static void
302t1_fix_bbox(BBox *bbox, const char *str, int len, int advance,
303 int space_position) {
304 /* never called with len == 0 */
305 if (str[0] == space_position && bbox->llx > 0)
306 bbox->llx = 0;
307 if (str[len-1] == space_position && bbox->urx < advance)
308 bbox->urx = advance;
309 if (bbox->lly > bbox->ury)
310 bbox->lly = bbox->ury = 0;
311}
02d1d628
AMH
312
313/*
314=item i_t1_bbox(handle, fontnum, points, str, len, cords)
315
316function to get a strings bounding box given the font id and sizes
317
318 handle - pointer to font handle
319 fontnum - t1 library font id
320 points - number of points in fontheight
321 str - string to measure
322 len - string length
323 cords - the bounding box (modified in place)
324
325=cut
326*/
327
3799c4d1 328int
97ac0a96 329i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) {
02d1d628
AMH
330 BBox bbox;
331 BBox gbbox;
1bd75e4c 332 int mod_flags = t1_get_flags(flags);
3799c4d1 333 int advance;
5eebac42 334 int space_position = T1_GetEncodingIndex(fontnum, "space");
02d1d628
AMH
335
336 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
337 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
5eebac42
TC
338
339 if (len == 0) {
340 /* len == 0 has special meaning to T1lib, but it means there's
341 nothing to draw, so return that */
342 bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
343 advance = 0;
1bd75e4c
TC
344 }
345 else {
5eebac42
TC
346 if (utf8) {
347 int worklen;
348 char *work = t1_from_utf8(str, len, &worklen);
c1b3b04b 349 advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
5eebac42
TC
350 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
351 t1_fix_bbox(&bbox, work, worklen, advance, space_position);
352 myfree(work);
353 }
354 else {
c1b3b04b 355 advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
5eebac42
TC
356 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
357 t1_fix_bbox(&bbox, str, len, advance, space_position);
358 }
1bd75e4c 359 }
02d1d628
AMH
360 gbbox = T1_GetFontBBox(fontnum);
361
362 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
363 (int)(bbox.llx*points/1000),
364 (int)(gbbox.lly*points/1000),
365 (int)(bbox.urx*points/1000),
366 (int)(gbbox.ury*points/1000),
367 (int)(bbox.lly*points/1000),
368 (int)(bbox.ury*points/1000) ));
369
370
3799c4d1
TC
371 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
372 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
373
374 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
375 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
376
377 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
378 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
02d1d628 379
3799c4d1 380 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
7fdbfba8
TC
381 cords[BBOX_RIGHT_BEARING] =
382 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
02d1d628 383
7fdbfba8 384 return BBOX_RIGHT_BEARING+1;
02d1d628
AMH
385}
386
387
388/*
389=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
390
391Interface to text rendering in a single color onto an image
392
393 im - pointer to image structure
394 xb - x coordinate of start of string
395 yb - y coordinate of start of string ( see align )
396 cl - color to draw the text in
397 fontnum - t1 library font id
398 points - number of points in fontheight
399 str - char pointer to string to render
400 len - string length
401 align - (0 - top of font glyph | 1 - baseline )
402
403=cut
404*/
405
406undef_int
97ac0a96 407i_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
AMH
408 GLYPH *glyph;
409 int xsize,ysize,x,y,ch;
410 i_color val;
411 unsigned char c,i;
1bd75e4c 412 int mod_flags = t1_get_flags(flags);
02d1d628
AMH
413
414 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
415
1bd75e4c
TC
416 if (utf8) {
417 int worklen;
418 char *work = t1_from_utf8(str, len, &worklen);
419 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
420 myfree(work);
421 }
422 else {
97ac0a96
TC
423 /* T1_AASetString() accepts a char * not a const char */
424 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
1bd75e4c 425 }
faa9b3e7
TC
426 if (glyph == NULL)
427 return 0;
02d1d628
AMH
428
429 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
430 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
431 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
432 mm_log((1,"bpp: %d\n",glyph->bpp));
433
434 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
435 ysize=glyph->metrics.ascent-glyph->metrics.descent;
436
437 mm_log((1,"width: %d height: %d\n",xsize,ysize));
438
439 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
440
441 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
442 c=glyph->bits[y*xsize+x];
443 i=255-c;
444 i_gpix(im,x+xb,y+yb,&val);
445 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
446 i_ppix(im,x+xb,y+yb,&val);
447 }
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
855static TT_Engine engine;
856static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
857static int LTT_hinted = 1; /* FIXME: this too */
858
859
860/*
861 * FreeType interface
862 */
863
864
865/*
866=item init_tt()
867
868Initializes the freetype font rendering engine
869
870=cut
871*/
872
873undef_int
b33c08f8 874i_init_tt() {
02d1d628 875 TT_Error error;
95b2bff4
TC
876 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
877
02d1d628
AMH
878 mm_log((1,"init_tt()\n"));
879 error = TT_Init_FreeType( &engine );
880 if ( error ){
881 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
882 return(1);
883 }
3799c4d1
TC
884
885#ifdef FTXPOST
886 error = TT_Init_Post_Extension( engine );
887 if (error) {
888 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
889 return 1;
890 }
891#endif
892
95b2bff4
TC
893 error = TT_Set_Raster_Gray_Palette(engine, palette);
894 if (error) {
895 mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
896 return 1;
897 }
898
02d1d628
AMH
899 return(0);
900}
901
902
903/*
904=item i_tt_get_instance(handle, points, smooth)
905
906Finds a points+smooth instance or if one doesn't exist in the cache
907allocates room and returns its cache entry
908
909 fontname - path to the font to load
910 handle - handle to the font.
911 points - points of the requested font
912 smooth - boolean (True: antialias on, False: antialias is off)
913
914=cut
915*/
916
917static
918int
919i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
920 int i,idx;
921 TT_Error error;
922
4f68b48f
TC
923 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
924 handle,points,smooth));
02d1d628
AMH
925
926 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
927 for(i=0;i<TT_CHC;i++) {
928 if (handle->instanceh[i].ptsize==points) {
929 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
930 return i;
931 }
02d1d628
AMH
932 }
933 smooth=1; /* We will be adding a font - add it as smooth then */
934 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
935 for(i=0;i<TT_CHC;i++) {
936 if (handle->instanceh[i].ptsize == points
937 && handle->instanceh[i].smooth == smooth) {
938 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
939 return i;
940 }
02d1d628
AMH
941 }
942 }
943
944 /* Found the instance in the cache - return the cache index */
945
4f68b48f
TC
946 for(idx=0;idx<TT_CHC;idx++) {
947 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
948 }
02d1d628
AMH
949
950 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
4f68b48f
TC
951 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
952 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
953
954 if ( USTRCT(handle->instanceh[idx].instance) ) {
955 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 956
4f68b48f 957 /* Free cached glyphs */
93d0372f 958 for(i=0;i<256;i++)
4f68b48f
TC
959 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
960 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 961
4f68b48f
TC
962 for(i=0;i<256;i++) {
963 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
964 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
965 }
966
967 /* Free instance if needed */
968 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
969 }
970
971 /* create and initialize instance */
972 /* FIXME: probably a memory leak on fail */
973
974 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
975 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
976 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
977
978 if ( error ) {
979 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
980 return -1;
981 }
982
983 /* Now that the instance should the inplace we need to lower all of the
984 ru counts and put `this' one with the highest entry */
985
986 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
987
988 handle->instanceh[idx].order=TT_CHC-1;
989 handle->instanceh[idx].ptsize=points;
990 handle->instanceh[idx].smooth=smooth;
991 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
992
4f68b48f
TC
993 /* Zero the memory for the glyph storage so they are not thought as
994 cached if they haven't been cached since this new font was loaded */
02d1d628 995
4f68b48f
TC
996 for(i=0;i<256;i++) {
997 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
998 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
999 }
02d1d628
AMH
1000
1001 return idx;
1002}
1003
1004
1005/*
1006=item i_tt_new(fontname)
1007
1008Creates a new font handle object, finds a character map and initialise the
1009the font handle's cache
1010
1011 fontname - path to the font to load
1012
1013=cut
1014*/
1015
1016TT_Fonthandle*
97ac0a96 1017i_tt_new(const char *fontname) {
02d1d628
AMH
1018 TT_Error error;
1019 TT_Fonthandle *handle;
1020 unsigned short i,n;
1021 unsigned short platform,encoding;
d93d5c10
TC
1022
1023 i_clear_error();
02d1d628
AMH
1024
1025 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1026
1027 /* allocate memory for the structure */
1028
f0960b14 1029 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
02d1d628
AMH
1030
1031 /* load the typeface */
1032 error = TT_Open_Face( engine, fontname, &handle->face );
1033 if ( error ) {
4f68b48f
TC
1034 if ( error == TT_Err_Could_Not_Open_File ) {
1035 mm_log((1, "Could not find/open %s.\n", fontname ));
1036 }
1037 else {
1038 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1039 error ));
1040 }
d93d5c10 1041 i_tt_push_error(error);
02d1d628
AMH
1042 return NULL;
1043 }
1044
1045 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 1046
02d1d628 1047 /* First, look for a Unicode charmap */
02d1d628
AMH
1048 n = handle->properties.num_CharMaps;
1049 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1050
1051 for ( i = 0; i < n; i++ ) {
1052 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
1053 if ( (platform == 3 && encoding == 1 )
1054 || (platform == 0 && encoding == 0 ) ) {
1055 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1056 platform, encoding));
02d1d628
AMH
1057 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1058 break;
1059 }
1060 }
27e79497
TC
1061 if (!USTRCT(handle->char_map) && n != 0) {
1062 /* just use the first one */
1063 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1064 }
02d1d628
AMH
1065
1066 /* Zero the pointsizes - and ordering */
1067
1068 for(i=0;i<TT_CHC;i++) {
1069 USTRCT(handle->instanceh[i].instance)=NULL;
1070 handle->instanceh[i].order=i;
1071 handle->instanceh[i].ptsize=0;
1072 handle->instanceh[i].smooth=-1;
1073 }
1074
3799c4d1
TC
1075#ifdef FTXPOST
1076 handle->loaded_names = 0;
1077#endif
1078
02d1d628
AMH
1079 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1080 return handle;
1081}
1082
1083
1084
1085/*
1086