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