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