]> git.imager.perl.org - imager.git/blame - font.c
change C<allow_partial> to C<allow_incomplete> to match the tag name
[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
AMH
875 TT_Error error;
876 mm_log((1,"init_tt()\n"));
877 error = TT_Init_FreeType( &engine );
878 if ( error ){
879 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
880 return(1);
881 }
3799c4d1
TC
882
883#ifdef FTXPOST
884 error = TT_Init_Post_Extension( engine );
885 if (error) {
886 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
887 return 1;
888 }
889#endif
890
02d1d628
AMH
891 return(0);
892}
893
894
895/*
896=item i_tt_get_instance(handle, points, smooth)
897
898Finds a points+smooth instance or if one doesn't exist in the cache
899allocates room and returns its cache entry
900
901 fontname - path to the font to load
902 handle - handle to the font.
903 points - points of the requested font
904 smooth - boolean (True: antialias on, False: antialias is off)
905
906=cut
907*/
908
909static
910int
911i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
912 int i,idx;
913 TT_Error error;
914
4f68b48f
TC
915 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
916 handle,points,smooth));
02d1d628
AMH
917
918 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
919 for(i=0;i<TT_CHC;i++) {
920 if (handle->instanceh[i].ptsize==points) {
921 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
922 return i;
923 }
02d1d628
AMH
924 }
925 smooth=1; /* We will be adding a font - add it as smooth then */
926 } else { /* 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 && handle->instanceh[i].smooth == smooth) {
930 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
931 return i;
932 }
02d1d628
AMH
933 }
934 }
935
936 /* Found the instance in the cache - return the cache index */
937
4f68b48f
TC
938 for(idx=0;idx<TT_CHC;idx++) {
939 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
940 }
02d1d628
AMH
941
942 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
4f68b48f
TC
943 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
944 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
945
946 if ( USTRCT(handle->instanceh[idx].instance) ) {
947 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 948
4f68b48f 949 /* Free cached glyphs */
93d0372f 950 for(i=0;i<256;i++)
4f68b48f
TC
951 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
952 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 953
4f68b48f
TC
954 for(i=0;i<256;i++) {
955 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
956 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
957 }
958
959 /* Free instance if needed */
960 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
961 }
962
963 /* create and initialize instance */
964 /* FIXME: probably a memory leak on fail */
965
966 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
967 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
968 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
969
970 if ( error ) {
971 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
972 return -1;
973 }
974
975 /* Now that the instance should the inplace we need to lower all of the
976 ru counts and put `this' one with the highest entry */
977
978 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
979
980 handle->instanceh[idx].order=TT_CHC-1;
981 handle->instanceh[idx].ptsize=points;
982 handle->instanceh[idx].smooth=smooth;
983 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
984
4f68b48f
TC
985 /* Zero the memory for the glyph storage so they are not thought as
986 cached if they haven't been cached since this new font was loaded */
02d1d628 987
4f68b48f
TC
988 for(i=0;i<256;i++) {
989 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
990 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
991 }
02d1d628
AMH
992
993 return idx;
994}
995
996
997/*
998=item i_tt_new(fontname)
999
1000Creates a new font handle object, finds a character map and initialise the
1001the font handle's cache
1002
1003 fontname - path to the font to load
1004
1005=cut
1006*/
1007
1008TT_Fonthandle*
97ac0a96 1009i_tt_new(const char *fontname) {
02d1d628
AMH
1010 TT_Error error;
1011 TT_Fonthandle *handle;
1012 unsigned short i,n;
1013 unsigned short platform,encoding;
d93d5c10
TC
1014
1015 i_clear_error();
02d1d628
AMH
1016
1017 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1018
1019 /* allocate memory for the structure */
1020
f0960b14 1021 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
02d1d628
AMH
1022
1023 /* load the typeface */
1024 error = TT_Open_Face( engine, fontname, &handle->face );
1025 if ( error ) {
4f68b48f
TC
1026 if ( error == TT_Err_Could_Not_Open_File ) {
1027 mm_log((1, "Could not find/open %s.\n", fontname ));
1028 }
1029 else {
1030 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1031 error ));
1032 }
d93d5c10 1033 i_tt_push_error(error);
02d1d628
AMH
1034 return NULL;
1035 }
1036
1037 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 1038
02d1d628 1039 /* First, look for a Unicode charmap */
02d1d628
AMH
1040 n = handle->properties.num_CharMaps;
1041 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1042
1043 for ( i = 0; i < n; i++ ) {
1044 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
1045 if ( (platform == 3 && encoding == 1 )
1046 || (platform == 0 && encoding == 0 ) ) {
1047 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1048 platform, encoding));
02d1d628
AMH
1049 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1050 break;
1051 }
1052 }
27e79497
TC
1053 if (!USTRCT(handle->char_map) && n != 0) {
1054 /* just use the first one */
1055 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1056 }
02d1d628
AMH
1057
1058 /* Zero the pointsizes - and ordering */
1059
1060 for(i=0;i<TT_CHC;i++) {
1061 USTRCT(handle->instanceh[i].instance)=NULL;
1062 handle->instanceh[i].order=i;
1063 handle->instanceh[i].ptsize=0;
1064 handle->instanceh[i].smooth=-1;
1065 }
1066
3799c4d1
TC
1067#ifdef FTXPOST
1068 handle->loaded_names = 0;
1069#endif
1070
02d1d628
AMH
1071 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1072 return handle;
1073}
1074
1075
1076
1077/*
1078