18 font.c - implements font handling functions for t1 and truetype fonts
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);
31 handle = i_tt_new(path_to_ttf);
32 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
39 font.c implements font creation, rendering, bounding box functions and
42 =head1 FUNCTION REFERENCE
44 Some of these functions are internal.
55 Initialize font rendering libraries if they are avaliable.
61 i_init_fonts(int t1log) {
62 mm_log((1,"Initializing fonts\n"));
77 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
85 static int t1_get_flags(char const *flags);
86 static char *t1_from_utf8(char const *in, int len, int *outlen);
88 static void t1_push_error(void);
90 static int t1_active_fonts = 0;
91 static int t1_initialized = 0;
94 =item i_init_t1(t1log)
96 Initializes the t1lib font rendering engine.
102 i_init_t1(int t1log) {
103 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
104 mm_log((1,"init_t1()\n"));
106 if (t1_active_fonts) {
107 mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
111 if (t1_initialized) {
116 init_flags |= LOGFILE;
117 if ((T1_InitLib(init_flags) == NULL)){
118 mm_log((1,"Initialization of t1lib failed\n"));
121 T1_SetLogLevel(T1LOG_DEBUG);
122 i_t1_set_aa(1); /* Default Antialias value */
133 Shuts the t1lib font rendering engine down.
135 This it seems that this function is never used.
148 =item i_t1_new(pfb, afm)
150 Loads the fonts with the given filenames, returns its font id
152 pfb - path to pfb file for font
153 afm - path to afm file for font
159 i_t1_new(char *pfb,char *afm) {
162 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
163 font_id = T1_AddFont(pfb);
165 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
170 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
171 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
180 =item i_t1_destroy(font_id)
182 Frees resources for a t1 font with given font id.
184 font_id - number of the font to free
190 i_t1_destroy(int font_id) {
191 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
195 return T1_DeleteFont(font_id);
200 =item i_t1_set_aa(st)
202 Sets the antialiasing level of the t1 library.
204 st - 0 = NONE, 1 = LOW, 2 = HIGH.
210 i_t1_set_aa(int st) {
212 unsigned long cst[17];
215 T1_AASetBitsPerPixel( 8 );
216 T1_AASetLevel( T1_AA_NONE );
217 T1_AANSetGrayValues( 0, 255 );
218 mm_log((1,"setting T1 antialias to none\n"));
221 T1_AASetBitsPerPixel( 8 );
222 T1_AASetLevel( T1_AA_LOW );
223 T1_AASetGrayValues( 0,65,127,191,255 );
224 mm_log((1,"setting T1 antialias to low\n"));
227 T1_AASetBitsPerPixel(8);
228 T1_AASetLevel(T1_AA_HIGH);
229 for(i=0;i<17;i++) cst[i]=(i*255)/16;
230 T1_AAHSetGrayValues( cst );
231 mm_log((1,"setting T1 antialias to high\n"));
237 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
239 Interface to text rendering into a single channel in an image
241 im pointer to image structure
242 xb x coordinate of start of string
243 yb y coordinate of start of string ( see align )
244 channel - destination channel
245 fontnum - t1 library font id
246 points - number of points in fontheight
247 str - string to render
249 align - (0 - top of font glyph | 1 - baseline )
255 i_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) {
259 int mod_flags = t1_get_flags(flags);
261 unsigned int ch_mask_store;
263 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
267 char *work = t1_from_utf8(str, len, &worklen);
268 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
272 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
277 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
278 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
279 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
280 mm_log((1,"bpp: %d\n",glyph->bpp));
282 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
283 ysize=glyph->metrics.ascent-glyph->metrics.descent;
285 mm_log((1,"width: %d height: %d\n",xsize,ysize));
287 ch_mask_store=im->ch_mask;
288 im->ch_mask=1<<channel;
290 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
292 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
293 val.channel[channel]=glyph->bits[y*xsize+x];
294 i_ppix(im,x+xb,y+yb,&val);
297 im->ch_mask=ch_mask_store;
302 t1_fix_bbox(BBox *bbox, const char *str, int len, int advance,
303 int space_position) {
304 /* never called with len == 0 */
305 if (str[0] == space_position && bbox->llx > 0)
307 if (str[len-1] == space_position && bbox->urx < advance)
309 if (bbox->lly > bbox->ury)
310 bbox->lly = bbox->ury = 0;
314 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
316 function to get a strings bounding box given the font id and sizes
318 handle - pointer to font handle
319 fontnum - t1 library font id
320 points - number of points in fontheight
321 str - string to measure
323 cords - the bounding box (modified in place)
329 i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) {
332 int mod_flags = t1_get_flags(flags);
334 int space_position = T1_GetEncodingIndex(fontnum, "space");
336 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
337 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
340 /* len == 0 has special meaning to T1lib, but it means there's
341 nothing to draw, so return that */
342 bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
348 char *work = t1_from_utf8(str, len, &worklen);
349 advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
350 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
351 t1_fix_bbox(&bbox, work, worklen, advance, space_position);
355 advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
356 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
357 t1_fix_bbox(&bbox, str, len, advance, space_position);
360 gbbox = T1_GetFontBBox(fontnum);
362 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
363 (int)(bbox.llx*points/1000),
364 (int)(gbbox.lly*points/1000),
365 (int)(bbox.urx*points/1000),
366 (int)(gbbox.ury*points/1000),
367 (int)(bbox.lly*points/1000),
368 (int)(bbox.ury*points/1000) ));
371 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
372 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
374 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
375 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
377 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
378 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
380 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
381 cords[BBOX_RIGHT_BEARING] =
382 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
384 return BBOX_RIGHT_BEARING+1;
389 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
391 Interface to text rendering in a single color onto an image
393 im - pointer to image structure
394 xb - x coordinate of start of string
395 yb - y coordinate of start of string ( see align )
396 cl - color to draw the text in
397 fontnum - t1 library font id
398 points - number of points in fontheight
399 str - char pointer to string to render
401 align - (0 - top of font glyph | 1 - baseline )
407 i_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) {
409 int xsize,ysize,x,y,ch;
412 int mod_flags = t1_get_flags(flags);
414 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
418 char *work = t1_from_utf8(str, len, &worklen);
419 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
423 /* T1_AASetString() accepts a char * not a const char */
424 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
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));
434 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
435 ysize=glyph->metrics.ascent-glyph->metrics.descent;
437 mm_log((1,"width: %d height: %d\n",xsize,ysize));
439 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
441 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
442 c=glyph->bits[y*xsize+x];
444 i_gpix(im,x+xb,y+yb,&val);
445 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
446 i_ppix(im,x+xb,y+yb,&val);
452 =item t1_get_flags(flags)
454 Processes the characters in I<flags> to create a mod_flags value used
455 by some T1Lib functions.
460 t1_get_flags(char const *flags) {
461 int mod_flags = T1_KERNING;
465 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
466 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
467 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
468 /* ignore anything we don't recognize */
476 =item t1_from_utf8(char const *in, int len, int *outlen)
478 Produces an unencoded version of I<in> by dropping any Unicode
481 Returns a newly allocated buffer which should be freed with myfree().
482 Sets *outlen to the number of bytes used in the output string.
488 t1_from_utf8(char const *in, int len, int *outlen) {
489 /* at this point len is from a perl SV, so can't approach MAXINT */
490 char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
495 c = i_utf8_advance(&in, &len);
498 i_push_error(0, "invalid UTF8 character");
501 /* yeah, just drop them */
513 =item i_t1_has_chars(font_num, text, len, utf8, out)
515 Check if the given characters are defined by the font. Note that len
516 is the number of bytes, not the number of characters (when utf8 is
519 out[char index] will be true if the character exists.
521 Accepts UTF-8, but since T1 can only have 256 characters, any chars
522 with values over 255 will simply be returned as false.
524 Returns the number of characters that were checked.
530 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
534 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
535 font_num, text, len, utf8));
538 if (T1_LoadFont(font_num)) {
546 c = i_utf8_advance(&text, &len);
548 i_push_error(0, "invalid UTF8 character");
553 c = (unsigned char)*text++;
558 /* limit of 256 characters for T1 */
562 char const * name = T1_GetCharName(font_num, (unsigned char)c);
565 *out++ = strcmp(name, ".notdef") != 0;
568 mm_log((2, " No name found for character %lx\n", c));
579 =item i_t1_face_name(font_num, name_buf, name_buf_size)
581 Copies the face name of the given C<font_num> to C<name_buf>. Returns
582 the number of characters required to store the name (which can be
583 larger than C<name_buf_size>, including the space required to store
584 the terminating NUL).
586 If name_buf is too small (as specified by name_buf_size) then the name
587 will be truncated. name_buf will always be NUL termintaed.
593 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
597 if (T1_LoadFont(font_num)) {
601 name = T1_GetFontName(font_num);
604 strncpy(name_buf, name, name_buf_size);
605 name_buf[name_buf_size-1] = '\0';
606 return strlen(name) + 1;
615 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
616 size_t name_buf_size) {
623 if (T1_LoadFont(font_num)) {
627 name = T1_GetCharName(font_num, (unsigned char)ch);
629 if (strcmp(name, ".notdef")) {
630 strncpy(name_buf, name, name_buf_size);
631 name_buf[name_buf_size-1] = '\0';
632 return strlen(name) + 1;
645 t1_push_error(void) {
648 i_push_error(0, "No error");
651 #ifdef T1ERR_SCAN_FONT_FORMAT
652 case T1ERR_SCAN_FONT_FORMAT:
653 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
657 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
658 case T1ERR_SCAN_FILE_OPEN_ERR:
659 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
663 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
664 case T1ERR_SCAN_OUT_OF_MEMORY:
665 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
669 #ifdef T1ERR_SCAN_ERROR
670 case T1ERR_SCAN_ERROR:
671 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
675 #ifdef T1ERR_SCAN_FILE_EOF
676 case T1ERR_SCAN_FILE_EOF:
677 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
681 #ifdef T1ERR_PATH_ERROR
682 case T1ERR_PATH_ERROR:
683 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
687 #ifdef T1ERR_PARSE_ERROR
688 case T1ERR_PARSE_ERROR:
689 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
693 #ifdef T1ERR_TYPE1_ABORT
694 case T1ERR_TYPE1_ABORT:
695 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
699 #ifdef T1ERR_INVALID_FONTID
700 case T1ERR_INVALID_FONTID:
701 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
705 #ifdef T1ERR_INVALID_PARAMETER
706 case T1ERR_INVALID_PARAMETER:
707 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
711 #ifdef T1ERR_OP_NOT_PERMITTED
712 case T1ERR_OP_NOT_PERMITTED:
713 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
717 #ifdef T1ERR_ALLOC_MEM
718 case T1ERR_ALLOC_MEM:
719 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
723 #ifdef T1ERR_FILE_OPEN_ERR
724 case T1ERR_FILE_OPEN_ERR:
725 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
729 #ifdef T1ERR_UNSPECIFIED
730 case T1ERR_UNSPECIFIED:
731 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
735 #ifdef T1ERR_NO_AFM_DATA
736 case T1ERR_NO_AFM_DATA:
737 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
743 i_push_error(T1ERR_X11, "X11");
747 #ifdef T1ERR_COMPOSITE_CHAR
748 case T1ERR_COMPOSITE_CHAR:
749 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
754 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
758 #endif /* HAVE_LIBT1 */
761 /* Truetype font support */
764 /* These are enabled by default when configuring Freetype 1.x
765 I haven't a clue how to reliably detect it at compile time.
767 We need a compilation probe in Makefile.PL
772 #include <freetype.h>
780 #include <ftxerr18.h>
783 /* some versions of FT1.x don't seem to define this - it's font defined
784 so it won't change */
785 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
786 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
789 /* convert a code point into an index in the glyph cache */
790 #define TT_HASH(x) ((x) & 0xFF)
792 typedef struct i_glyph_entry_ {
797 #define TT_NOCHAR (~0UL)
799 struct TT_Instancehandle_ {
800 TT_Instance instance;
801 TT_Instance_Metrics imetrics;
802 TT_Glyph_Metrics gmetrics[256];
803 i_tt_glyph_entry glyphs[256];
809 typedef struct TT_Instancehandle_ TT_Instancehandle;
811 struct TT_Fonthandle_ {
813 TT_Face_Properties properties;
814 TT_Instancehandle instanceh[TT_CHC];
824 #define USTRCT(x) ((x).z)
825 #define TT_VALID( handle ) ( ( handle ).z != NULL )
827 static void i_tt_push_error(TT_Error rc);
831 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
832 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
833 static void i_tt_done_raster_map( TT_Raster_Map *bit );
834 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
835 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
836 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
838 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
839 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
840 int x_off, int y_off, int smooth );
842 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
843 TT_Raster_Map *small_bit, int cords[6],
844 char const* txt, int len, int smooth, int utf8 );
845 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
846 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
848 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
849 float points, char const* txt, int len, int smooth, int utf8 );
850 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
853 /* static globals needed */
855 static TT_Engine engine;
856 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
857 static int LTT_hinted = 1; /* FIXME: this too */
868 Initializes the freetype font rendering engine
876 mm_log((1,"init_tt()\n"));
877 error = TT_Init_FreeType( &engine );
879 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
884 error = TT_Init_Post_Extension( engine );
886 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
896 =item i_tt_get_instance(handle, points, smooth)
898 Finds a points+smooth instance or if one doesn't exist in the cache
899 allocates room and returns its cache entry
901 fontname - path to the font to load
902 handle - handle to the font.
903 points - points of the requested font
904 smooth - boolean (True: antialias on, False: antialias is off)
911 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
915 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
916 handle,points,smooth));
918 if (smooth == -1) { /* Smooth doesn't matter for this search */
919 for(i=0;i<TT_CHC;i++) {
920 if (handle->instanceh[i].ptsize==points) {
921 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
925 smooth=1; /* We will be adding a font - add it as smooth then */
926 } else { /* Smooth doesn't matter for this search */
927 for(i=0;i<TT_CHC;i++) {
928 if (handle->instanceh[i].ptsize == points
929 && handle->instanceh[i].smooth == smooth) {
930 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
936 /* Found the instance in the cache - return the cache index */
938 for(idx=0;idx<TT_CHC;idx++) {
939 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
942 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
943 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
944 USTRCT(handle->instanceh[idx].instance) ));
946 if ( USTRCT(handle->instanceh[idx].instance) ) {
947 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
949 /* Free cached glyphs */
951 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
952 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
955 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
956 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
959 /* Free instance if needed */
960 TT_Done_Instance( handle->instanceh[idx].instance );
963 /* create and initialize instance */
964 /* FIXME: probably a memory leak on fail */
966 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
967 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
968 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
971 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
975 /* Now that the instance should the inplace we need to lower all of the
976 ru counts and put `this' one with the highest entry */
978 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
980 handle->instanceh[idx].order=TT_CHC-1;
981 handle->instanceh[idx].ptsize=points;
982 handle->instanceh[idx].smooth=smooth;
983 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
985 /* Zero the memory for the glyph storage so they are not thought as
986 cached if they haven't been cached since this new font was loaded */
989 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
990 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
998 =item i_tt_new(fontname)
1000 Creates a new font handle object, finds a character map and initialise the
1001 the font handle's cache
1003 fontname - path to the font to load
1009 i_tt_new(const char *fontname) {
1011 TT_Fonthandle *handle;
1013 unsigned short platform,encoding;
1017 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1019 /* allocate memory for the structure */
1021 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
1023 /* load the typeface */
1024 error = TT_Open_Face( engine, fontname, &handle->face );
1026 if ( error == TT_Err_Could_Not_Open_File ) {
1027 mm_log((1, "Could not find/open %s.\n", fontname ));
1030 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1033 i_tt_push_error(error);
1037 TT_Get_Face_Properties( handle->face, &(handle->properties) );
1039 /* First, look for a Unicode charmap */
1040 n = handle->properties.num_CharMaps;
1041 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1043 for ( i = 0; i < n; i++ ) {
1044 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
1045 if ( (platform == 3 && encoding == 1 )
1046 || (platform == 0 && encoding == 0 ) ) {
1047 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1048 platform, encoding));
1049 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1053 if (!USTRCT(handle->char_map) && n != 0) {
1054 /* just use the first one */
1055 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1058 /* Zero the pointsizes - and ordering */
1060 for(i=0;i<TT_CHC;i++) {
1061 USTRCT(handle->instanceh[i].instance)=NULL;
1062 handle->instanceh[i].order=i;
1063 handle->instanceh[i].ptsize=0;
1064 handle->instanceh[i].smooth=-1;
1068 handle->loaded_names = 0;
1071 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1078 * raster map management
1082 =item i_tt_init_raster_map(bit, width, height, smooth)
1084 Allocates internal memory for the bitmap as needed by the parameters (internal)
1086 bit - bitmap to allocate into
1087 width - width of the bitmap
1088 height - height of the bitmap
1089 smooth - boolean (True: antialias on, False: antialias is off)
1096 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1098 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1101 bit->width = ( width + 3 ) & -4;
1102 bit->flow = TT_Flow_Down;
1105 bit->cols = bit->width;
1106 bit->size = bit->rows * bit->width;
1108 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1109 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1112 /* rows can be 0 for some glyphs, for example ' ' */
1113 if (bit->rows && bit->size / bit->rows != bit->cols) {
1114 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1115 bit->width, bit->rows);
1118 mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %d)\n", bit->width, bit->cols, bit->rows, bit->size ));
1120 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1121 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1126 =item i_tt_clear_raster_map(bit)
1128 Frees the bitmap data and sets pointer to NULL (internal)
1130 bit - bitmap to free
1137 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1138 myfree( bit->bitmap );
1144 =item i_tt_clear_raster_map(bit)
1146 Clears the specified bitmap (internal)
1148 bit - bitmap to zero
1156 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1157 memset( bit->bitmap, 0, bit->size );
1162 =item i_tt_blit_or(dst, src, x_off, y_off)
1164 function that blits one raster map into another (internal)
1166 dst - destination bitmap
1168 x_off - x offset into the destination bitmap
1169 y_off - y offset into the destination bitmap
1176 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1181 x1 = x_off < 0 ? -x_off : 0;
1182 y1 = y_off < 0 ? -y_off : 0;
1184 x2 = (int)dst->cols - x_off;
1185 if ( x2 > src->cols ) x2 = src->cols;
1187 y2 = (int)dst->rows - y_off;
1188 if ( y2 > src->rows ) y2 = src->rows;
1190 if ( x1 >= x2 ) return;
1192 /* do the real work now */
1194 for ( y = y1; y < y2; ++y ) {
1195 s = ( (char*)src->bitmap ) + y * src->cols + x1;
1196 d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1198 for ( x = x1; x < x2; ++x ) {
1207 /* useful for debugging */
1210 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1212 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1213 for (y = 0; y < bit->rows; ++y) {
1214 fprintf(out, "%2d:", y);
1215 for (x = 0; x < bit->cols; ++x) {
1216 if ((x & 7) == 0 && x) putc(' ', out);
1217 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1226 =item i_tt_get_glyph(handle, inst, j)
1228 Function to see if a glyph exists and if so cache it (internal)
1230 handle - pointer to font handle
1231 inst - font instance
1232 j - charcode of glyph
1239 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1240 unsigned short load_flags, code;
1243 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1244 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1246 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1248 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1249 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1250 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1254 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1255 /* clean up the entry */
1256 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1257 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1258 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1261 /* Ok - it wasn't cached - try to get it in */
1262 load_flags = TTLOAD_SCALE_GLYPH;
1263 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1265 if ( !TT_VALID(handle->char_map) ) {
1266 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1267 if ( code >= handle->properties.num_Glyphs ) code = 0;
1268 } else code = TT_Char_Index( handle->char_map, j );
1270 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1271 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1272 i_push_error(error, "TT_New_Glyph()");
1275 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1276 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1278 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1279 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1280 i_push_error(error, "TT_Load_Glyph()");
1284 /* At this point the glyph should be allocated and loaded */
1285 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1287 /* Next get the glyph metrics */
1288 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1289 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1291 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1292 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1293 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1294 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1295 i_push_error(error, "TT_Get_Glyph_Metrics()");
1303 =item i_tt_has_chars(handle, text, len, utf8, out)
1305 Check if the given characters are defined by the font. Note that len
1306 is the number of bytes, not the number of characters (when utf8 is
1309 Returns the number of characters that were checked.
1315 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1318 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1319 handle, text, len, utf8));
1325 c = i_utf8_advance(&text, &len);
1327 i_push_error(0, "invalid UTF8 character");
1332 c = (unsigned char)*text++;
1336 if (TT_VALID(handle->char_map)) {
1337 index = TT_Char_Index(handle->char_map, c);
1340 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1341 if (index >= handle->properties.num_Glyphs)
1344 *out++ = index != 0;
1352 =item i_tt_destroy(handle)
1354 Clears the data taken by a font including all cached data such as
1357 handle - pointer to font handle
1363 i_tt_destroy( TT_Fonthandle *handle) {
1364 TT_Close_Face( handle->face );
1367 /* FIXME: Should these be freed automatically by the library?
1369 TT_Done_Instance( instance );
1371 i_tt_done_glyphs( void ) {
1374 if ( !glyphs ) return;
1376 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1386 * FreeType Rendering functions
1391 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1393 Renders a single glyph into the bit rastermap (internal)
1395 handle - pointer to font handle
1396 gmetrics - the metrics for the glyph to be rendered
1397 bit - large bitmap that is the destination for the text
1398 smallbit - small bitmap that is used only if smooth is true
1399 x_off - x offset of glyph
1400 y_off - y offset of glyph
1401 smooth - boolean (True: antialias on, False: antialias is off)
1408 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth ) {
1410 mm_log((1,"i_tt_render_glyph(glyph 0x0%X, gmetrics 0x0%X, bit 0x%X, small_bit 0x%X, x_off %d, y_off %d, smooth %d)\n",
1411 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1413 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1415 TT_F26Dot6 xmin, ymin, xmax, ymax;
1417 xmin = gmetrics->bbox.xMin & -64;
1418 ymin = gmetrics->bbox.yMin & -64;
1419 xmax = (gmetrics->bbox.xMax + 63) & -64;
1420 ymax = (gmetrics->bbox.yMax + 63) & -64;
1422 i_tt_clear_raster_map( small_bit );
1423 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1424 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1430 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1432 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1434 handle - pointer to font handle
1435 inst - font instance
1436 bit - large bitmap that is the destination for the text
1437 smallbit - small bitmap that is used only if smooth is true
1438 txt - string to render
1439 len - length of the string to render
1440 smooth - boolean (True: antialias on, False: antialias is off)
1447 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1448 TT_Raster_Map *small_bit, int cords[6],
1449 char const* txt, int len, int smooth, int utf8 ) {
1453 mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d, utf8 %d)\n",
1454 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1457 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1460 x=-cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */
1465 j = i_utf8_advance(&txt, &len);
1467 i_push_error(0, "invalid UTF8 character");
1472 j = (unsigned char)*txt++;
1475 if ( !i_tt_get_glyph(handle,inst,j) )
1477 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1478 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1479 small_bit, x, y, smooth );
1480 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1488 * Functions to render rasters (single channel images) onto images
1492 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1494 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1496 im - image to dump raster on
1497 bit - bitmap that contains the text to be dumped to im
1498 xb, yb - coordinates, left edge and baseline
1499 cl - color to use for text
1500 smooth - boolean (True: antialias on, False: antialias is off)
1507 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
1511 mm_log((1,"i_tt_dump_raster_map2(im 0x%x, bit 0x%X, xb %d, yb %d, cl 0x%X)\n",im,bit,xb,yb,cl));
1513 bmap = (char *)bit->bitmap;
1517 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1518 c=(255*bmap[y*(bit->cols)+x])/4;
1520 i_gpix(im,x+xb,y+yb,&val);
1521 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1522 i_ppix(im,x+xb,y+yb,&val);
1527 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1528 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1530 i_gpix(im,x+xb,y+yb,&val);
1531 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1532 i_ppix(im,x+xb,y+yb,&val);
1540 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1542 Function to dump a raster onto a single channel image in color (internal)
1544 im - image to dump raster on
1545 bit - bitmap that contains the text to be dumped to im
1546 xb, yb - coordinates, left edge and baseline
1547 channel - channel to copy to
1548 smooth - boolean (True: antialias on, False: antialias is off)
1555 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1560 mm_log((1,"i_tt_dump_raster_channel(im 0x%x, bit 0x%X, xb %d, yb %d, channel %d)\n",im,bit,xb,yb,channel));
1562 bmap = (char *)bit->bitmap;
1565 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1566 c=(255*bmap[y*(bit->cols)+x])/4;
1567 i_gpix(im,x+xb,y+yb,&val);
1568 val.channel[channel]=c;
1569 i_ppix(im,x+xb,y+yb,&val);
1572 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1573 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1574 i_gpix(im,x+xb,y+yb,&val);
1575 val.channel[channel]=c;
1576 i_ppix(im,x+xb,y+yb,&val);
1583 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1585 interface for generating single channel raster of text (internal)
1587 handle - pointer to font handle
1588 bit - the bitmap that is allocated, rendered into and NOT freed
1589 cords - the bounding box (modified in place)
1590 points - font size to use
1591 txt - string to render
1592 len - length of the string to render
1593 smooth - boolean (True: antialias on, False: antialias is off)
1600 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1603 TT_Raster_Map small_bit;
1605 /* find or install an instance */
1606 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1607 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1611 /* calculate bounding box */
1612 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1616 width = cords[2]-cords[0];
1617 height = cords[5]-cords[4];
1619 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1621 i_tt_init_raster_map ( bit, width, height, smooth );
1622 i_tt_clear_raster_map( bit );
1623 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1625 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1628 i_tt_done_raster_map( &small_bit );
1632 if ( smooth ) i_tt_done_raster_map( &small_bit );
1639 * Exported text rendering interfaces
1644 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1646 Interface to text rendering into a single channel in an image
1648 handle - pointer to font handle
1649 im - image to render text on to
1650 xb, yb - coordinates, left edge and baseline
1651 channel - channel to render into
1652 points - font size to use
1653 txt - string to render
1654 len - length of the string to render
1655 smooth - boolean (True: antialias on, False: antialias is off)
1661 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8, int align ) {
1663 int cords[BOUNDING_BOX_COUNT];
1664 int ascent, st_offset, y;
1668 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1670 ascent=cords[BBOX_ASCENT];
1671 st_offset=cords[BBOX_NEG_WIDTH];
1672 y = align ? yb-ascent : yb;
1674 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1675 i_tt_done_raster_map( &bit );
1682 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1684 Interface to text rendering in a single color onto an image
1686 handle - pointer to font handle
1687 im - image to render text on to
1688 xb, yb - coordinates, left edge and baseline
1689 cl - color to use for text
1690 points - font size to use
1691 txt - string to render
1692 len - length of the string to render
1693 smooth - boolean (True: antialias on, False: antialias is off)
1699 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) {
1700 int cords[BOUNDING_BOX_COUNT];
1701 int ascent, st_offset, y;
1706 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1708 ascent=cords[BBOX_ASCENT];
1709 st_offset=cords[BBOX_NEG_WIDTH];
1710 y = align ? yb-ascent : yb;
1712 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1713 i_tt_done_raster_map( &bit );
1720 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1722 Function to get texts bounding boxes given the instance of the font (internal)
1724 handle - pointer to font handle
1725 inst - font instance
1726 txt - string to measure
1727 len - length of the string to render
1728 cords - the bounding box (modified in place)
1735 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1736 int upm, casc, cdesc, first;
1747 unsigned char *ustr;
1748 ustr=(unsigned char*)txt;
1750 mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d, utf8 %d)\n",handle,inst,len,txt,len, utf8));
1752 upm = handle->properties.header->Units_Per_EM;
1753 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1754 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1759 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1764 j = i_utf8_advance(&txt, &len);
1766 i_push_error(0, "invalid UTF8 character");
1771 j = (unsigned char)*txt++;
1774 if ( i_tt_get_glyph(handle,inst,j) ) {
1775 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1776 width += gm->advance / 64;
1777 casc = (gm->bbox.yMax+63) / 64;
1778 cdesc = (gm->bbox.yMin-63) / 64;
1780 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1781 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1784 start = gm->bbox.xMin / 64;
1785 ascent = (gm->bbox.yMax+63) / 64;
1786 descent = (gm->bbox.yMin-63) / 64;
1789 if (!len) { /* if at end of string */
1790 /* the right-side bearing - in case the right-side of a
1791 character goes past the right of the advance width,
1792 as is common for italic fonts
1794 rightb = gm->advance - gm->bearingX
1795 - (gm->bbox.xMax - gm->bbox.xMin);
1796 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1797 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1800 ascent = (ascent > casc ? ascent : casc );
1801 descent = (descent < cdesc ? descent : cdesc);
1805 cords[BBOX_NEG_WIDTH]=start;
1806 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1807 cords[BBOX_POS_WIDTH]=width;
1809 cords[BBOX_POS_WIDTH] -= rightb / 64;
1810 cords[BBOX_GLOBAL_ASCENT]=gascent;
1811 cords[BBOX_DESCENT]=descent;
1812 cords[BBOX_ASCENT]=ascent;
1813 cords[BBOX_ADVANCE_WIDTH] = width;
1814 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1816 return BBOX_RIGHT_BEARING + 1;
1821 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1823 Interface to get a strings bounding box
1825 handle - pointer to font handle
1826 points - font size to use
1827 txt - string to render
1828 len - length of the string to render
1829 cords - the bounding box (modified in place)
1835 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8) {
1839 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1841 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1842 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1843 mm_log((1,"i_tt_text: get instance failed\n"));
1847 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1851 =item i_tt_face_name(handle, name_buf, name_buf_size)
1853 Retrieve's the font's postscript name.
1855 This is complicated by the need to handle encodings and so on.
1860 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1861 TT_Face_Properties props;
1864 TT_UShort platform_id, encoding_id, lang_id, name_id;
1867 int want_index = -1; /* an acceptable but not perfect name */
1872 TT_Get_Face_Properties(handle->face, &props);
1873 name_count = props.num_Names;
1874 for (i = 0; i < name_count; ++i) {
1875 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1878 TT_Get_Name_String(handle->face, i, &name, &name_len);
1880 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1881 && name_id == TT_NAME_ID_PS_NAME) {
1882 int might_want_index = -1;
1883 int might_score = 0;
1884 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1886 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1887 /* exactly what we want */
1892 if (platform_id == TT_PLATFORM_MICROSOFT
1893 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1894 /* any english is good */
1895 might_want_index = i;
1898 /* there might be something in between */
1900 /* anything non-unicode is better than nothing */
1901 might_want_index = i;
1904 if (might_score > score) {
1905 score = might_score;
1906 want_index = might_want_index;
1911 if (want_index != -1) {
1912 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1914 strncpy(name_buf, name, name_buf_size);
1915 name_buf[name_buf_size-1] = '\0';
1917 return strlen(name) + 1;
1920 i_push_error(0, "no face name present");
1925 void i_tt_dump_names(TT_Fonthandle *handle) {
1926 TT_Face_Properties props;
1929 TT_UShort platform_id, encoding_id, lang_id, name_id;
1933 TT_Get_Face_Properties(handle->face, &props);
1934 name_count = props.num_Names;
1935 for (i = 0; i < name_count; ++i) {
1936 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1938 TT_Get_Name_String(handle->face, i, &name, &name_len);
1940 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1941 encoding_id, lang_id, name_id);
1942 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1943 printf("(unicode)\n");
1946 printf("'%s'\n", name);
1952 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1953 size_t name_buf_size) {
1961 if (!handle->loaded_names) {
1963 mm_log((1, "Loading PS Names"));
1964 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1965 ++handle->loaded_names;
1968 if (handle->load_cond) {
1969 i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1973 index = TT_Char_Index(handle->char_map, ch);
1975 i_push_error(0, "no such character");
1979 rc = TT_Get_PS_Name(handle->face, index, &psname);
1982 i_push_error(rc, "error getting name");
1986 strncpy(name_buf, psname, name_buf_size);
1987 name_buf[name_buf_size-1] = '\0';
1989 return strlen(psname) + 1;
1991 mm_log((1, "FTXPOST extension not enabled\n"));
1993 i_push_error(0, "Use of FTXPOST extension disabled");
2000 =item i_tt_push_error(code)
2002 Push an error message and code onto the Imager error stack.
2007 i_tt_push_error(TT_Error rc) {
2009 TT_String const *msg = TT_ErrToString18(rc);
2011 i_push_error(rc, msg);
2013 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
2017 #endif /* HAVE_LIBTT */
2025 Arnar M. Hrafnkelsson <addi@umich.edu>