- limit limits.c to C89
[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;
7fdbfba8
TC
331 cords[BBOX_RIGHT_BEARING] =
332 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
02d1d628 333
7fdbfba8 334 return BBOX_RIGHT_BEARING+1;
02d1d628
AMH
335}
336
337
338/*
339=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
340
341Interface to text rendering in a single color onto an image
342
343 im - pointer to image structure
344 xb - x coordinate of start of string
345 yb - y coordinate of start of string ( see align )
346 cl - color to draw the text in
347 fontnum - t1 library font id
348 points - number of points in fontheight
349 str - char pointer to string to render
350 len - string length
351 align - (0 - top of font glyph | 1 - baseline )
352
353=cut
354*/
355
356undef_int
1bd75e4c 357i_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
358 GLYPH *glyph;
359 int xsize,ysize,x,y,ch;
360 i_color val;
361 unsigned char c,i;
1bd75e4c 362 int mod_flags = t1_get_flags(flags);
02d1d628
AMH
363
364 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
365
1bd75e4c
TC
366 if (utf8) {
367 int worklen;
368 char *work = t1_from_utf8(str, len, &worklen);
369 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
370 myfree(work);
371 }
372 else {
373 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
374 }
faa9b3e7
TC
375 if (glyph == NULL)
376 return 0;
02d1d628
AMH
377
378 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
379 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
380 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
381 mm_log((1,"bpp: %d\n",glyph->bpp));
382
383 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
384 ysize=glyph->metrics.ascent-glyph->metrics.descent;
385
386 mm_log((1,"width: %d height: %d\n",xsize,ysize));
387
388 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
389
390 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
391 c=glyph->bits[y*xsize+x];
392 i=255-c;
393 i_gpix(im,x+xb,y+yb,&val);
394 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
395 i_ppix(im,x+xb,y+yb,&val);
396 }
397 return 1;
398}
399
1bd75e4c
TC
400/*
401=item t1_get_flags(flags)
402
403Processes the characters in I<flags> to create a mod_flags value used
404by some T1Lib functions.
405
406=cut
407 */
408static int
409t1_get_flags(char const *flags) {
410 int mod_flags = T1_KERNING;
411
412 while (*flags) {
413 switch (*flags++) {
414 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
415 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
416 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
417 /* ignore anything we don't recognize */
418 }
419 }
420
421 return mod_flags;
422}
423
424/*
425=item t1_from_utf8(char const *in, int len, int *outlen)
426
427Produces an unencoded version of I<in> by dropping any Unicode
428character over 255.
429
430Returns a newly allocated buffer which should be freed with myfree().
431Sets *outlen to the number of bytes used in the output string.
432
433=cut
434*/
435
436static char *
437t1_from_utf8(char const *in, int len, int *outlen) {
438 char *out = mymalloc(len+1);
439 char *p = out;
440 unsigned long c;
441
442 while (len) {
443 c = i_utf8_advance(&in, &len);
444 if (c == ~0UL) {
445 myfree(out);
446 i_push_error(0, "invalid UTF8 character");
447 return 0;
448 }
449 /* yeah, just drop them */
450 if (c < 0x100) {
451 *p++ = (char)c;
452 }
453 }
454 *p = '\0';
455 *outlen = p - out;
456
457 return out;
458}
02d1d628 459
3799c4d1
TC
460/*
461=item i_t1_has_chars(font_num, text, len, utf8, out)
462
463Check if the given characters are defined by the font. Note that len
464is the number of bytes, not the number of characters (when utf8 is
465non-zero).
466
467out[char index] will be true if the character exists.
468
469Accepts UTF-8, but since T1 can only have 256 characters, any chars
470with values over 255 will simply be returned as false.
471
472Returns the number of characters that were checked.
473
474=cut
475*/
476
477int
478i_t1_has_chars(int font_num, const char *text, int len, int utf8,
479 char *out) {
480 int count = 0;
481
482 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
483 font_num, text, len, utf8));
484
485 i_clear_error();
486 if (T1_LoadFont(font_num)) {
487 t1_push_error();
488 return 0;
489 }
490
491 while (len) {
492 unsigned long c;
3799c4d1
TC
493 if (utf8) {
494 c = i_utf8_advance(&text, &len);
495 if (c == ~0UL) {
496 i_push_error(0, "invalid UTF8 character");
497 return 0;
498 }
499 }
500 else {
501 c = (unsigned char)*text++;
502 --len;
503 }
504
505 if (c >= 0x100) {
506 /* limit of 256 characters for T1 */
507 *out++ = 0;
508 }
509 else {
510 char const * name = T1_GetCharName(font_num, (unsigned char)c);
511
512 if (name) {
513 *out++ = strcmp(name, ".notdef") != 0;
514 }
515 else {
516 mm_log((2, " No name found for character %lx\n", c));
517 *out++ = 0;
518 }
519 }
520 ++count;
521 }
522
523 return count;
524}
525
526/*
527=item i_t1_face_name(font_num, name_buf, name_buf_size)
528
529Copies the face name of the given C<font_num> to C<name_buf>. Returns
530the number of characters required to store the name (which can be
531larger than C<name_buf_size>, including the space required to store
532the terminating NUL).
533
534If name_buf is too small (as specified by name_buf_size) then the name
535will be truncated. name_buf will always be NUL termintaed.
536
537=cut
538*/
539
540int
541i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
542 char *name;
543
544 T1_errno = 0;
545 if (T1_LoadFont(font_num)) {
546 t1_push_error();
547 return 0;
548 }
549 name = T1_GetFontName(font_num);
550
551 if (name) {
552 strncpy(name_buf, name, name_buf_size);
553 name_buf[name_buf_size-1] = '\0';
554 return strlen(name) + 1;
555 }
556 else {
557 t1_push_error();
558 return 0;
559 }
560}
561
562int
563i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
564 size_t name_buf_size) {
565 char *name;
566
567 i_clear_error();
568 if (ch > 0xFF) {
569 return 0;
570 }
571 if (T1_LoadFont(font_num)) {
572 t1_push_error();
573 return 0;
574 }
575 name = T1_GetCharName(font_num, (unsigned char)ch);
576 if (name) {
577 if (strcmp(name, ".notdef")) {
578 strncpy(name_buf, name, name_buf_size);
579 name_buf[name_buf_size-1] = '\0';
580 return strlen(name) + 1;
581 }
582 else {
583 return 0;
584 }
585 }
586 else {
587 t1_push_error();
588 return 0;
589 }
590}
591
592static void
593t1_push_error(void) {
594 switch (T1_errno) {
595 case 0:
596 i_push_error(0, "No error");
597 break;
598
d5a3f460 599#ifdef T1ERR_SCAN_FONT_FORMAT
3799c4d1
TC
600 case T1ERR_SCAN_FONT_FORMAT:
601 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
602 break;
d5a3f460 603#endif
3799c4d1 604
d5a3f460 605#ifdef T1ERR_SCAN_FILE_OPEN_ERR
3799c4d1
TC
606 case T1ERR_SCAN_FILE_OPEN_ERR:
607 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
608 break;
d5a3f460 609#endif
3799c4d1 610
d5a3f460 611#ifdef T1ERR_SCAN_OUT_OF_MEMORY
3799c4d1
TC
612 case T1ERR_SCAN_OUT_OF_MEMORY:
613 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
614 break;
d5a3f460 615#endif
3799c4d1 616
d5a3f460 617#ifdef T1ERR_SCAN_ERROR
3799c4d1
TC
618 case T1ERR_SCAN_ERROR:
619 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
620 break;
d5a3f460 621#endif
3799c4d1 622
d5a3f460 623#ifdef T1ERR_SCAN_FILE_EOF
3799c4d1
TC
624 case T1ERR_SCAN_FILE_EOF:
625 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
626 break;
d5a3f460 627#endif
3799c4d1 628
d5a3f460 629#ifdef T1ERR_PATH_ERROR
3799c4d1
TC
630 case T1ERR_PATH_ERROR:
631 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
632 break;
d5a3f460 633#endif
3799c4d1 634
d5a3f460 635#ifdef T1ERR_PARSE_ERROR
3799c4d1
TC
636 case T1ERR_PARSE_ERROR:
637 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
638 break;
d5a3f460 639#endif
3799c4d1 640
d5a3f460 641#ifdef T1ERR_TYPE1_ABORT
3799c4d1
TC
642 case T1ERR_TYPE1_ABORT:
643 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
644 break;
d5a3f460 645#endif
3799c4d1 646
d5a3f460 647#ifdef T1ERR_INVALID_FONTID
3799c4d1
TC
648 case T1ERR_INVALID_FONTID:
649 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
650 break;
d5a3f460 651#endif
3799c4d1 652
d5a3f460 653#ifdef T1ERR_INVALID_PARAMETER
3799c4d1
TC
654 case T1ERR_INVALID_PARAMETER:
655 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
656 break;
d5a3f460 657#endif
3799c4d1 658
d5a3f460 659#ifdef T1ERR_OP_NOT_PERMITTED
3799c4d1
TC
660 case T1ERR_OP_NOT_PERMITTED:
661 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
662 break;
d5a3f460 663#endif
3799c4d1 664
d5a3f460 665#ifdef T1ERR_ALLOC_MEM
3799c4d1
TC
666 case T1ERR_ALLOC_MEM:
667 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
668 break;
d5a3f460 669#endif
3799c4d1 670
d5a3f460 671#ifdef T1ERR_FILE_OPEN_ERR
3799c4d1
TC
672 case T1ERR_FILE_OPEN_ERR:
673 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
674 break;
d5a3f460 675#endif
3799c4d1 676
d5a3f460 677#ifdef T1ERR_UNSPECIFIED
3799c4d1
TC
678 case T1ERR_UNSPECIFIED:
679 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
680 break;
d5a3f460 681#endif
3799c4d1 682
d5a3f460 683#ifdef T1ERR_NO_AFM_DATA
3799c4d1
TC
684 case T1ERR_NO_AFM_DATA:
685 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
686 break;
d5a3f460 687#endif
3799c4d1 688
d5a3f460 689#ifdef T1ERR_X11
3799c4d1
TC
690 case T1ERR_X11:
691 i_push_error(T1ERR_X11, "X11");
692 break;
d5a3f460 693#endif
3799c4d1 694
d5a3f460 695#ifdef T1ERR_COMPOSITE_CHAR
3799c4d1
TC
696 case T1ERR_COMPOSITE_CHAR:
697 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
698 break;
d5a3f460 699#endif
3799c4d1
TC
700
701 default:
702 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
703 }
704}
705
02d1d628
AMH
706#endif /* HAVE_LIBT1 */
707
708
4f68b48f 709/* Truetype font support */
4f68b48f 710#ifdef HAVE_LIBTT
02d1d628 711
3799c4d1
TC
712/* This is enabled by default when configuring Freetype 1.x
713 I haven't a clue how to reliably detect it at compile time.
714
715 We need a compilation probe in Makefile.PL
716*/
717#define FTXPOST 1
718
4f68b48f
TC
719#include <freetype.h>
720#define TT_CHC 5
02d1d628 721
3799c4d1
TC
722#ifdef FTXPOST
723#include <ftxpost.h>
724#endif
725
a90b253d
TC
726/* some versions of FT1.x don't seem to define this - it's font defined
727 so it won't change */
728#ifndef TT_MS_LANGID_ENGLISH_GENERAL
729#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
730#endif
731
4f68b48f
TC
732/* convert a code point into an index in the glyph cache */
733#define TT_HASH(x) ((x) & 0xFF)
02d1d628 734
4f68b48f
TC
735typedef struct i_glyph_entry_ {
736 TT_Glyph glyph;
737 unsigned long ch;
738} i_tt_glyph_entry;
02d1d628 739
4f68b48f 740#define TT_NOCHAR (~0UL)
02d1d628 741
4f68b48f
TC
742struct TT_Instancehandle_ {
743 TT_Instance instance;
744 TT_Instance_Metrics imetrics;
745 TT_Glyph_Metrics gmetrics[256];
746 i_tt_glyph_entry glyphs[256];
747 int smooth;
748 int ptsize;
749 int order;
750};
02d1d628 751
4f68b48f 752typedef struct TT_Instancehandle_ TT_Instancehandle;
02d1d628 753
4f68b48f
TC
754struct TT_Fonthandle_ {
755 TT_Face face;
756 TT_Face_Properties properties;
757 TT_Instancehandle instanceh[TT_CHC];
758 TT_CharMap char_map;
3799c4d1
TC
759#ifdef FTXPOST
760 int loaded_names;
761 TT_Error load_cond;
762#endif
4f68b48f 763};
02d1d628
AMH
764
765/* Defines */
766
767#define USTRCT(x) ((x).z)
768#define TT_VALID( handle ) ( ( handle ).z != NULL )
769
770
771/* Prototypes */
772
773static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
774static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
775static void i_tt_done_raster_map( TT_Raster_Map *bit );
776static void i_tt_clear_raster_map( TT_Raster_Map* bit );
777static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
4f68b48f
TC
778static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
779static void
780i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
781 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
782 int x_off, int y_off, int smooth );
783static int
784i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
785 TT_Raster_Map *small_bit, int cords[6],
786 char const* txt, int len, int smooth, int utf8 );
02d1d628
AMH
787static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
788static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
4f68b48f
TC
789static int
790i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
791 float points, char const* txt, int len, int smooth, int utf8 );
792static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
02d1d628
AMH
793
794
795/* static globals needed */
796
797static TT_Engine engine;
798static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
799static int LTT_hinted = 1; /* FIXME: this too */
800
801
802/*
803 * FreeType interface
804 */
805
806
807/*
808=item init_tt()
809
810Initializes the freetype font rendering engine
811
812=cut
813*/
814
815undef_int
b33c08f8 816i_init_tt() {
02d1d628
AMH
817 TT_Error error;
818 mm_log((1,"init_tt()\n"));
819 error = TT_Init_FreeType( &engine );
820 if ( error ){
821 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
822 return(1);
823 }
3799c4d1
TC
824
825#ifdef FTXPOST
826 error = TT_Init_Post_Extension( engine );
827 if (error) {
828 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
829 return 1;
830 }
831#endif
832
02d1d628
AMH
833 return(0);
834}
835
836
837/*
838=item i_tt_get_instance(handle, points, smooth)
839
840Finds a points+smooth instance or if one doesn't exist in the cache
841allocates room and returns its cache entry
842
843 fontname - path to the font to load
844 handle - handle to the font.
845 points - points of the requested font
846 smooth - boolean (True: antialias on, False: antialias is off)
847
848=cut
849*/
850
851static
852int
853i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
854 int i,idx;
855 TT_Error error;
856
4f68b48f
TC
857 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
858 handle,points,smooth));
02d1d628
AMH
859
860 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
861 for(i=0;i<TT_CHC;i++) {
862 if (handle->instanceh[i].ptsize==points) {
863 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
864 return i;
865 }
02d1d628
AMH
866 }
867 smooth=1; /* We will be adding a font - add it as smooth then */
868 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
869 for(i=0;i<TT_CHC;i++) {
870 if (handle->instanceh[i].ptsize == points
871 && handle->instanceh[i].smooth == smooth) {
872 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
873 return i;
874 }
02d1d628
AMH
875 }
876 }
877
878 /* Found the instance in the cache - return the cache index */
879
4f68b48f
TC
880 for(idx=0;idx<TT_CHC;idx++) {
881 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
882 }
02d1d628
AMH
883
884 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
4f68b48f
TC
885 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
886 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
887
888 if ( USTRCT(handle->instanceh[idx].instance) ) {
889 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 890
4f68b48f 891 /* Free cached glyphs */
93d0372f 892 for(i=0;i<256;i++)
4f68b48f
TC
893 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
894 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 895
4f68b48f
TC
896 for(i=0;i<256;i++) {
897 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
898 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
899 }
900
901 /* Free instance if needed */
902 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
903 }
904
905 /* create and initialize instance */
906 /* FIXME: probably a memory leak on fail */
907
908 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
909 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
910 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
911
912 if ( error ) {
913 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
914 return -1;
915 }
916
917 /* Now that the instance should the inplace we need to lower all of the
918 ru counts and put `this' one with the highest entry */
919
920 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
921
922 handle->instanceh[idx].order=TT_CHC-1;
923 handle->instanceh[idx].ptsize=points;
924 handle->instanceh[idx].smooth=smooth;
925 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
926
4f68b48f
TC
927 /* Zero the memory for the glyph storage so they are not thought as
928 cached if they haven't been cached since this new font was loaded */
02d1d628 929
4f68b48f
TC
930 for(i=0;i<256;i++) {
931 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
932 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
933 }
02d1d628
AMH
934
935 return idx;
936}
937
938
939/*
940=item i_tt_new(fontname)
941
942Creates a new font handle object, finds a character map and initialise the
943the font handle's cache
944
945 fontname - path to the font to load
946
947=cut
948*/
949
950TT_Fonthandle*
951i_tt_new(char *fontname) {
952 TT_Error error;
953 TT_Fonthandle *handle;
954 unsigned short i,n;
955 unsigned short platform,encoding;
956
957 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
958
959 /* allocate memory for the structure */
960
4b19f77a 961 handle = mymalloc( sizeof(TT_Fonthandle) );
02d1d628
AMH
962
963 /* load the typeface */
964 error = TT_Open_Face( engine, fontname, &handle->face );
965 if ( error ) {
4f68b48f
TC
966 if ( error == TT_Err_Could_Not_Open_File ) {
967 mm_log((1, "Could not find/open %s.\n", fontname ));
968 }
969 else {
970 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
971 error ));
972 }
02d1d628
AMH
973 return NULL;
974 }
975
976 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 977
02d1d628 978 /* First, look for a Unicode charmap */
02d1d628
AMH
979 n = handle->properties.num_CharMaps;
980 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
981
982 for ( i = 0; i < n; i++ ) {
983 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
984 if ( (platform == 3 && encoding == 1 )
985 || (platform == 0 && encoding == 0 ) ) {
986 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
987 platform, encoding));
02d1d628
AMH
988 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
989 break;
990 }
991 }
27e79497
TC
992 if (!USTRCT(handle->char_map) && n != 0) {
993 /* just use the first one */
994 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
995 }
02d1d628
AMH
996
997 /* Zero the pointsizes - and ordering */
998
999 for(i=0;i<TT_CHC;i++) {
1000 USTRCT(handle->instanceh[i].instance)=NULL;
1001 handle->instanceh[i].order=i;
1002 handle->instanceh[i].ptsize=0;
1003 handle->instanceh[i].smooth=-1;
1004 }
1005
3799c4d1
TC
1006#ifdef FTXPOST
1007 handle->loaded_names = 0;
1008#endif
1009
02d1d628
AMH
1010 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1011 return handle;
1012}
1013
1014
1015
1016/*
1017