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