13 font.c - implements font handling functions for t1 and truetype fonts
20 fontnum = i_t1_new(path_to_pfb, path_to_afm);
21 i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
22 rc = i_t1_destroy(fontnum);
26 handle = i_tt_new(path_to_ttf);
27 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
34 font.c implements font creation, rendering, bounding box functions and
37 =head1 FUNCTION REFERENCE
39 Some of these functions are internal.
50 Initialize font rendering libraries if they are avaliable.
56 i_init_fonts(int t1log) {
57 mm_log((1,"Initializing fonts\n"));
72 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
80 static int t1_get_flags(char const *flags);
81 static char *t1_from_utf8(char const *in, int len, int *outlen);
83 static void t1_push_error(void);
86 =item i_init_t1(t1log)
88 Initializes the t1lib font rendering engine.
94 i_init_t1(int t1log) {
95 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
96 mm_log((1,"init_t1()\n"));
99 init_flags |= LOGFILE;
100 if ((T1_InitLib(init_flags) == NULL)){
101 mm_log((1,"Initialization of t1lib failed\n"));
104 T1_SetLogLevel(T1LOG_DEBUG);
105 i_t1_set_aa(1); /* Default Antialias value */
113 Shuts the t1lib font rendering engine down.
115 This it seems that this function is never used.
127 =item i_t1_new(pfb, afm)
129 Loads the fonts with the given filenames, returns its font id
131 pfb - path to pfb file for font
132 afm - path to afm file for font
138 i_t1_new(char *pfb,char *afm) {
141 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
142 font_id = T1_AddFont(pfb);
144 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
149 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
150 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
157 =item i_t1_destroy(font_id)
159 Frees resources for a t1 font with given font id.
161 font_id - number of the font to free
167 i_t1_destroy(int font_id) {
168 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
169 return T1_DeleteFont(font_id);
174 =item i_t1_set_aa(st)
176 Sets the antialiasing level of the t1 library.
178 st - 0 = NONE, 1 = LOW, 2 = HIGH.
184 i_t1_set_aa(int st) {
186 unsigned long cst[17];
189 T1_AASetBitsPerPixel( 8 );
190 T1_AASetLevel( T1_AA_NONE );
191 T1_AANSetGrayValues( 0, 255 );
192 mm_log((1,"setting T1 antialias to none\n"));
195 T1_AASetBitsPerPixel( 8 );
196 T1_AASetLevel( T1_AA_LOW );
197 T1_AASetGrayValues( 0,65,127,191,255 );
198 mm_log((1,"setting T1 antialias to low\n"));
201 T1_AASetBitsPerPixel(8);
202 T1_AASetLevel(T1_AA_HIGH);
203 for(i=0;i<17;i++) cst[i]=(i*255)/16;
204 T1_AAHSetGrayValues( cst );
205 mm_log((1,"setting T1 antialias to high\n"));
211 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
213 Interface to text rendering into a single channel in an image
215 im pointer to image structure
216 xb x coordinate of start of string
217 yb y coordinate of start of string ( see align )
218 channel - destination channel
219 fontnum - t1 library font id
220 points - number of points in fontheight
221 str - string to render
223 align - (0 - top of font glyph | 1 - baseline )
229 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) {
233 int mod_flags = t1_get_flags(flags);
235 unsigned int ch_mask_store;
237 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
241 char *work = t1_from_utf8(str, len, &worklen);
242 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
246 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
251 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
252 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
253 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
254 mm_log((1,"bpp: %d\n",glyph->bpp));
256 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
257 ysize=glyph->metrics.ascent-glyph->metrics.descent;
259 mm_log((1,"width: %d height: %d\n",xsize,ysize));
261 ch_mask_store=im->ch_mask;
262 im->ch_mask=1<<channel;
264 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
266 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
267 val.channel[channel]=glyph->bits[y*xsize+x];
268 i_ppix(im,x+xb,y+yb,&val);
271 im->ch_mask=ch_mask_store;
277 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
279 function to get a strings bounding box given the font id and sizes
281 handle - pointer to font handle
282 fontnum - t1 library font id
283 points - number of points in fontheight
284 str - string to measure
286 cords - the bounding box (modified in place)
292 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
295 int mod_flags = t1_get_flags(flags);
298 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
299 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
302 char *work = t1_from_utf8(str, len, &worklen);
303 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
307 bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
309 gbbox = T1_GetFontBBox(fontnum);
310 advance = T1_GetStringWidth(fontnum, str, len, 0, mod_flags);
312 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
313 (int)(bbox.llx*points/1000),
314 (int)(gbbox.lly*points/1000),
315 (int)(bbox.urx*points/1000),
316 (int)(gbbox.ury*points/1000),
317 (int)(bbox.lly*points/1000),
318 (int)(bbox.ury*points/1000) ));
321 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
322 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
324 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
325 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
327 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
328 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
330 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
332 return BBOX_ADVANCE_WIDTH+1;
337 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
339 Interface to text rendering in a single color onto an image
341 im - pointer to image structure
342 xb - x coordinate of start of string
343 yb - y coordinate of start of string ( see align )
344 cl - color to draw the text in
345 fontnum - t1 library font id
346 points - number of points in fontheight
347 str - char pointer to string to render
349 align - (0 - top of font glyph | 1 - baseline )
355 i_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) {
357 int xsize,ysize,x,y,ch;
360 int mod_flags = t1_get_flags(flags);
362 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
366 char *work = t1_from_utf8(str, len, &worklen);
367 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
371 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
376 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
377 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
378 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
379 mm_log((1,"bpp: %d\n",glyph->bpp));
381 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
382 ysize=glyph->metrics.ascent-glyph->metrics.descent;
384 mm_log((1,"width: %d height: %d\n",xsize,ysize));
386 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
388 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
389 c=glyph->bits[y*xsize+x];
391 i_gpix(im,x+xb,y+yb,&val);
392 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
393 i_ppix(im,x+xb,y+yb,&val);
399 =item t1_get_flags(flags)
401 Processes the characters in I<flags> to create a mod_flags value used
402 by some T1Lib functions.
407 t1_get_flags(char const *flags) {
408 int mod_flags = T1_KERNING;
412 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
413 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
414 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
415 /* ignore anything we don't recognize */
423 =item t1_from_utf8(char const *in, int len, int *outlen)
425 Produces an unencoded version of I<in> by dropping any Unicode
428 Returns a newly allocated buffer which should be freed with myfree().
429 Sets *outlen to the number of bytes used in the output string.
435 t1_from_utf8(char const *in, int len, int *outlen) {
436 char *out = mymalloc(len+1);
441 c = i_utf8_advance(&in, &len);
444 i_push_error(0, "invalid UTF8 character");
447 /* yeah, just drop them */
459 =item i_t1_has_chars(font_num, text, len, utf8, out)
461 Check if the given characters are defined by the font. Note that len
462 is the number of bytes, not the number of characters (when utf8 is
465 out[char index] will be true if the character exists.
467 Accepts UTF-8, but since T1 can only have 256 characters, any chars
468 with values over 255 will simply be returned as false.
470 Returns the number of characters that were checked.
476 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
480 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
481 font_num, text, len, utf8));
484 if (T1_LoadFont(font_num)) {
493 c = i_utf8_advance(&text, &len);
495 i_push_error(0, "invalid UTF8 character");
500 c = (unsigned char)*text++;
505 /* limit of 256 characters for T1 */
509 char const * name = T1_GetCharName(font_num, (unsigned char)c);
512 *out++ = strcmp(name, ".notdef") != 0;
515 mm_log((2, " No name found for character %lx\n", c));
526 =item i_t1_face_name(font_num, name_buf, name_buf_size)
528 Copies the face name of the given C<font_num> to C<name_buf>. Returns
529 the number of characters required to store the name (which can be
530 larger than C<name_buf_size>, including the space required to store
531 the terminating NUL).
533 If name_buf is too small (as specified by name_buf_size) then the name
534 will be truncated. name_buf will always be NUL termintaed.
540 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
544 if (T1_LoadFont(font_num)) {
548 name = T1_GetFontName(font_num);
551 strncpy(name_buf, name, name_buf_size);
552 name_buf[name_buf_size-1] = '\0';
553 return strlen(name) + 1;
562 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
563 size_t name_buf_size) {
570 if (T1_LoadFont(font_num)) {
574 name = T1_GetCharName(font_num, (unsigned char)ch);
576 if (strcmp(name, ".notdef")) {
577 strncpy(name_buf, name, name_buf_size);
578 name_buf[name_buf_size-1] = '\0';
579 return strlen(name) + 1;
592 t1_push_error(void) {
595 i_push_error(0, "No error");
598 #ifdef T1ERR_SCAN_FONT_FORMAT
599 case T1ERR_SCAN_FONT_FORMAT:
600 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
604 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
605 case T1ERR_SCAN_FILE_OPEN_ERR:
606 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
610 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
611 case T1ERR_SCAN_OUT_OF_MEMORY:
612 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
616 #ifdef T1ERR_SCAN_ERROR
617 case T1ERR_SCAN_ERROR:
618 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
622 #ifdef T1ERR_SCAN_FILE_EOF
623 case T1ERR_SCAN_FILE_EOF:
624 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
628 #ifdef T1ERR_PATH_ERROR
629 case T1ERR_PATH_ERROR:
630 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
634 #ifdef T1ERR_PARSE_ERROR
635 case T1ERR_PARSE_ERROR:
636 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
640 #ifdef T1ERR_TYPE1_ABORT
641 case T1ERR_TYPE1_ABORT:
642 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
646 #ifdef T1ERR_INVALID_FONTID
647 case T1ERR_INVALID_FONTID:
648 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
652 #ifdef T1ERR_INVALID_PARAMETER
653 case T1ERR_INVALID_PARAMETER:
654 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
658 #ifdef T1ERR_OP_NOT_PERMITTED
659 case T1ERR_OP_NOT_PERMITTED:
660 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
664 #ifdef T1ERR_ALLOC_MEM
665 case T1ERR_ALLOC_MEM:
666 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
670 #ifdef T1ERR_FILE_OPEN_ERR
671 case T1ERR_FILE_OPEN_ERR:
672 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
676 #ifdef T1ERR_UNSPECIFIED
677 case T1ERR_UNSPECIFIED:
678 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
682 #ifdef T1ERR_NO_AFM_DATA
683 case T1ERR_NO_AFM_DATA:
684 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
690 i_push_error(T1ERR_X11, "X11");
694 #ifdef T1ERR_COMPOSITE_CHAR
695 case T1ERR_COMPOSITE_CHAR:
696 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
701 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
705 #endif /* HAVE_LIBT1 */
708 /* Truetype font support */
711 /* This is enabled by default when configuring Freetype 1.x
712 I haven't a clue how to reliably detect it at compile time.
714 We need a compilation probe in Makefile.PL
718 #include <freetype.h>
725 /* some versions of FT1.x don't seem to define this - it's font defined
726 so it won't change */
727 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
728 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
731 /* convert a code point into an index in the glyph cache */
732 #define TT_HASH(x) ((x) & 0xFF)
734 typedef struct i_glyph_entry_ {
739 #define TT_NOCHAR (~0UL)
741 struct TT_Instancehandle_ {
742 TT_Instance instance;
743 TT_Instance_Metrics imetrics;
744 TT_Glyph_Metrics gmetrics[256];
745 i_tt_glyph_entry glyphs[256];
751 typedef struct TT_Instancehandle_ TT_Instancehandle;
753 struct TT_Fonthandle_ {
755 TT_Face_Properties properties;
756 TT_Instancehandle instanceh[TT_CHC];
766 #define USTRCT(x) ((x).z)
767 #define TT_VALID( handle ) ( ( handle ).z != NULL )
772 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
773 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
774 static void i_tt_done_raster_map( TT_Raster_Map *bit );
775 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
776 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
777 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
779 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
780 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
781 int x_off, int y_off, int smooth );
783 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
784 TT_Raster_Map *small_bit, int cords[6],
785 char const* txt, int len, int smooth, int utf8 );
786 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
787 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
789 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
790 float points, char const* txt, int len, int smooth, int utf8 );
791 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
794 /* static globals needed */
796 static TT_Engine engine;
797 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
798 static int LTT_hinted = 1; /* FIXME: this too */
809 Initializes the freetype font rendering engine
817 mm_log((1,"init_tt()\n"));
818 error = TT_Init_FreeType( &engine );
820 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
825 error = TT_Init_Post_Extension( engine );
827 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
837 =item i_tt_get_instance(handle, points, smooth)
839 Finds a points+smooth instance or if one doesn't exist in the cache
840 allocates room and returns its cache entry
842 fontname - path to the font to load
843 handle - handle to the font.
844 points - points of the requested font
845 smooth - boolean (True: antialias on, False: antialias is off)
852 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
856 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
857 handle,points,smooth));
859 if (smooth == -1) { /* Smooth doesn't matter for this search */
860 for(i=0;i<TT_CHC;i++) {
861 if (handle->instanceh[i].ptsize==points) {
862 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
866 smooth=1; /* We will be adding a font - add it as smooth then */
867 } else { /* Smooth doesn't matter for this search */
868 for(i=0;i<TT_CHC;i++) {
869 if (handle->instanceh[i].ptsize == points
870 && handle->instanceh[i].smooth == smooth) {
871 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
877 /* Found the instance in the cache - return the cache index */
879 for(idx=0;idx<TT_CHC;idx++) {
880 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
883 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
884 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
885 USTRCT(handle->instanceh[idx].instance) ));
887 if ( USTRCT(handle->instanceh[idx].instance) ) {
888 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
890 /* Free cached glyphs */
892 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
893 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
896 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
897 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
900 /* Free instance if needed */
901 TT_Done_Instance( handle->instanceh[idx].instance );
904 /* create and initialize instance */
905 /* FIXME: probably a memory leak on fail */
907 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
908 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
909 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
912 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
916 /* Now that the instance should the inplace we need to lower all of the
917 ru counts and put `this' one with the highest entry */
919 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
921 handle->instanceh[idx].order=TT_CHC-1;
922 handle->instanceh[idx].ptsize=points;
923 handle->instanceh[idx].smooth=smooth;
924 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
926 /* Zero the memory for the glyph storage so they are not thought as
927 cached if they haven't been cached since this new font was loaded */
930 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
931 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
939 =item i_tt_new(fontname)
941 Creates a new font handle object, finds a character map and initialise the
942 the font handle's cache
944 fontname - path to the font to load
950 i_tt_new(char *fontname) {
952 TT_Fonthandle *handle;
954 unsigned short platform,encoding;
956 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
958 /* allocate memory for the structure */
960 handle = mymalloc( sizeof(TT_Fonthandle) );
962 /* load the typeface */
963 error = TT_Open_Face( engine, fontname, &handle->face );
965 if ( error == TT_Err_Could_Not_Open_File ) {
966 mm_log((1, "Could not find/open %s.\n", fontname ));
969 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
975 TT_Get_Face_Properties( handle->face, &(handle->properties) );
977 /* First, look for a Unicode charmap */
978 n = handle->properties.num_CharMaps;
979 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
981 for ( i = 0; i < n; i++ ) {
982 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
983 if ( (platform == 3 && encoding == 1 )
984 || (platform == 0 && encoding == 0 ) ) {
985 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
986 platform, encoding));
987 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
991 if (!USTRCT(handle->char_map) && n != 0) {
992 /* just use the first one */
993 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
996 /* Zero the pointsizes - and ordering */
998 for(i=0;i<TT_CHC;i++) {
999 USTRCT(handle->instanceh[i].instance)=NULL;
1000 handle->instanceh[i].order=i;
1001 handle->instanceh[i].ptsize=0;
1002 handle->instanceh[i].smooth=-1;
1006 handle->loaded_names = 0;
1009 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1016 * raster map management
1020 =item i_tt_init_raster_map(bit, width, height, smooth)
1022 Allocates internal memory for the bitmap as needed by the parameters (internal)
1024 bit - bitmap to allocate into
1025 width - width of the bitmap
1026 height - height of the bitmap
1027 smooth - boolean (True: antialias on, False: antialias is off)
1034 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1036 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1039 bit->width = ( width + 3 ) & -4;
1040 bit->flow = TT_Flow_Down;
1043 bit->cols = bit->width;
1044 bit->size = bit->rows * bit->width;
1046 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1047 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1050 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 ));
1052 bit->bitmap = (void *) mymalloc( bit->size );
1053 if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1058 =item i_tt_clear_raster_map(bit)
1060 Frees the bitmap data and sets pointer to NULL (internal)
1062 bit - bitmap to free
1069 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1070 myfree( bit->bitmap );
1076 =item i_tt_clear_raster_map(bit)
1078 Clears the specified bitmap (internal)
1080 bit - bitmap to zero
1088 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1089 memset( bit->bitmap, 0, bit->size );
1094 =item i_tt_blit_or(dst, src, x_off, y_off)
1096 function that blits one raster map into another (internal)
1098 dst - destination bitmap
1100 x_off - x offset into the destination bitmap
1101 y_off - y offset into the destination bitmap
1108 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1113 x1 = x_off < 0 ? -x_off : 0;
1114 y1 = y_off < 0 ? -y_off : 0;
1116 x2 = (int)dst->cols - x_off;
1117 if ( x2 > src->cols ) x2 = src->cols;
1119 y2 = (int)dst->rows - y_off;
1120 if ( y2 > src->rows ) y2 = src->rows;
1122 if ( x1 >= x2 ) return;
1124 /* do the real work now */
1126 for ( y = y1; y < y2; ++y ) {
1127 s = ( (char*)src->bitmap ) + y * src->cols + x1;
1128 d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1130 for ( x = x1; x < x2; ++x ) {
1139 /* useful for debugging */
1142 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1144 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1145 for (y = 0; y < bit->rows; ++y) {
1146 fprintf(out, "%2d:", y);
1147 for (x = 0; x < bit->cols; ++x) {
1148 if ((x & 7) == 0 && x) putc(' ', out);
1149 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1158 =item i_tt_get_glyph(handle, inst, j)
1160 Function to see if a glyph exists and if so cache it (internal)
1162 handle - pointer to font handle
1163 inst - font instance
1164 j - charcode of glyph
1171 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1172 unsigned short load_flags, code;
1175 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1176 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1178 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1180 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1181 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1182 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1186 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1187 /* clean up the entry */
1188 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1189 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1190 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1193 /* Ok - it wasn't cached - try to get it in */
1194 load_flags = TTLOAD_SCALE_GLYPH;
1195 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1197 if ( !TT_VALID(handle->char_map) ) {
1198 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1199 if ( code >= handle->properties.num_Glyphs ) code = 0;
1200 } else code = TT_Char_Index( handle->char_map, j );
1202 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1203 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1204 i_push_error(error, "TT_New_Glyph()");
1207 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1208 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1210 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1211 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1212 i_push_error(error, "TT_Load_Glyph()");
1216 /* At this point the glyph should be allocated and loaded */
1217 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1219 /* Next get the glyph metrics */
1220 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1221 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1223 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1224 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1225 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1226 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1227 i_push_error(error, "TT_Get_Glyph_Metrics()");
1235 =item i_tt_has_chars(handle, text, len, utf8, out)
1237 Check if the given characters are defined by the font. Note that len
1238 is the number of bytes, not the number of characters (when utf8 is
1241 Returns the number of characters that were checked.
1247 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1251 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1252 handle, text, len, utf8));
1258 c = i_utf8_advance(&text, &len);
1260 i_push_error(0, "invalid UTF8 character");
1265 c = (unsigned char)*text++;
1269 if (TT_VALID(handle->char_map)) {
1270 index = TT_Char_Index(handle->char_map, c);
1273 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1274 if (index >= handle->properties.num_Glyphs)
1277 *out++ = index != 0;
1285 =item i_tt_destroy(handle)
1287 Clears the data taken by a font including all cached data such as
1290 handle - pointer to font handle
1296 i_tt_destroy( TT_Fonthandle *handle) {
1297 TT_Close_Face( handle->face );
1300 /* FIXME: Should these be freed automatically by the library?
1302 TT_Done_Instance( instance );
1304 i_tt_done_glyphs( void ) {
1307 if ( !glyphs ) return;
1309 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1319 * FreeType Rendering functions
1324 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1326 Renders a single glyph into the bit rastermap (internal)
1328 handle - pointer to font handle
1329 gmetrics - the metrics for the glyph to be rendered
1330 bit - large bitmap that is the destination for the text
1331 smallbit - small bitmap that is used only if smooth is true
1332 x_off - x offset of glyph
1333 y_off - y offset of glyph
1334 smooth - boolean (True: antialias on, False: antialias is off)
1341 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 ) {
1343 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",
1344 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1346 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1348 TT_F26Dot6 xmin, ymin, xmax, ymax;
1350 xmin = gmetrics->bbox.xMin & -64;
1351 ymin = gmetrics->bbox.yMin & -64;
1352 xmax = (gmetrics->bbox.xMax + 63) & -64;
1353 ymax = (gmetrics->bbox.yMax + 63) & -64;
1355 i_tt_clear_raster_map( small_bit );
1356 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1357 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1363 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1365 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1367 handle - pointer to font handle
1368 inst - font instance
1369 bit - large bitmap that is the destination for the text
1370 smallbit - small bitmap that is used only if smooth is true
1371 txt - string to render
1372 len - length of the string to render
1373 smooth - boolean (True: antialias on, False: antialias is off)
1380 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1381 TT_Raster_Map *small_bit, int cords[6],
1382 char const* txt, int len, int smooth, int utf8 ) {
1387 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",
1388 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1391 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1394 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 */
1399 j = i_utf8_advance(&txt, &len);
1401 i_push_error(0, "invalid UTF8 character");
1406 j = (unsigned char)*txt++;
1409 if ( !i_tt_get_glyph(handle,inst,j) )
1411 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1412 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1413 small_bit, x, y, smooth );
1414 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1422 * Functions to render rasters (single channel images) onto images
1426 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1428 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1430 im - image to dump raster on
1431 bit - bitmap that contains the text to be dumped to im
1432 xb, yb - coordinates, left edge and baseline
1433 cl - color to use for text
1434 smooth - boolean (True: antialias on, False: antialias is off)
1441 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1445 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));
1447 bmap = (char *)bit->bitmap;
1451 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1452 c=(255*bmap[y*(bit->cols)+x])/4;
1454 i_gpix(im,x+xb,y+yb,&val);
1455 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1456 i_ppix(im,x+xb,y+yb,&val);
1461 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1462 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1464 i_gpix(im,x+xb,y+yb,&val);
1465 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1466 i_ppix(im,x+xb,y+yb,&val);
1474 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1476 Function to dump a raster onto a single channel image in color (internal)
1478 im - image to dump raster on
1479 bit - bitmap that contains the text to be dumped to im
1480 xb, yb - coordinates, left edge and baseline
1481 channel - channel to copy to
1482 smooth - boolean (True: antialias on, False: antialias is off)
1489 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1494 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));
1496 bmap = (char *)bit->bitmap;
1499 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1500 c=(255*bmap[y*(bit->cols)+x])/4;
1501 i_gpix(im,x+xb,y+yb,&val);
1502 val.channel[channel]=c;
1503 i_ppix(im,x+xb,y+yb,&val);
1506 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1507 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1508 i_gpix(im,x+xb,y+yb,&val);
1509 val.channel[channel]=c;
1510 i_ppix(im,x+xb,y+yb,&val);
1517 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1519 interface for generating single channel raster of text (internal)
1521 handle - pointer to font handle
1522 bit - the bitmap that is allocated, rendered into and NOT freed
1523 cords - the bounding box (modified in place)
1524 points - font size to use
1525 txt - string to render
1526 len - length of the string to render
1527 smooth - boolean (True: antialias on, False: antialias is off)
1534 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1537 TT_Raster_Map small_bit;
1539 /* find or install an instance */
1540 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1541 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1545 /* calculate bounding box */
1546 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1550 width = cords[2]-cords[0];
1551 height = cords[5]-cords[4];
1553 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1555 i_tt_init_raster_map ( bit, width, height, smooth );
1556 i_tt_clear_raster_map( bit );
1557 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1559 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1562 i_tt_done_raster_map( &small_bit );
1566 /* ascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */
1568 if ( smooth ) i_tt_done_raster_map( &small_bit );
1575 * Exported text rendering interfaces
1580 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1582 Interface to text rendering into a single channel in an image
1584 handle - pointer to font handle
1585 im - image to render text on to
1586 xb, yb - coordinates, left edge and baseline
1587 channel - channel to render into
1588 points - font size to use
1589 txt - string to render
1590 len - length of the string to render
1591 smooth - boolean (True: antialias on, False: antialias is off)
1597 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 ) {
1599 int cords[BOUNDING_BOX_COUNT];
1600 int ascent, st_offset;
1604 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1609 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth );
1610 i_tt_done_raster_map( &bit );
1617 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1619 Interface to text rendering in a single color onto an image
1621 handle - pointer to font handle
1622 im - image to render text on to
1623 xb, yb - coordinates, left edge and baseline
1624 cl - color to use for text
1625 points - font size to use
1626 txt - string to render
1627 len - length of the string to render
1628 smooth - boolean (True: antialias on, False: antialias is off)
1634 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8) {
1635 int cords[BOUNDING_BOX_COUNT];
1636 int ascent, st_offset;
1641 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1646 i_tt_dump_raster_map2( im, &bit, xb+st_offset, yb-ascent, cl, smooth );
1647 i_tt_done_raster_map( &bit );
1654 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1656 Function to get texts bounding boxes given the instance of the font (internal)
1658 handle - pointer to font handle
1659 inst - font instance
1660 txt - string to measure
1661 len - length of the string to render
1662 cords - the bounding box (modified in place)
1669 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1670 int i, upm, casc, cdesc, first;
1681 unsigned char *ustr;
1682 ustr=(unsigned char*)txt;
1684 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));
1686 upm = handle->properties.header->Units_Per_EM;
1687 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1688 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1693 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1698 j = i_utf8_advance(&txt, &len);
1700 i_push_error(0, "invalid UTF8 character");
1705 j = (unsigned char)*txt++;
1708 if ( i_tt_get_glyph(handle,inst,j) ) {
1709 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1710 width += gm->advance / 64;
1711 casc = (gm->bbox.yMax+63) / 64;
1712 cdesc = (gm->bbox.yMin-63) / 64;
1714 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1715 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1718 start = gm->bbox.xMin / 64;
1719 ascent = (gm->bbox.yMax+63) / 64;
1720 descent = (gm->bbox.yMin-63) / 64;
1724 /* the right-side bearing - in case the right-side of a
1725 character goes past the right of the advance width,
1726 as is common for italic fonts
1728 rightb = gm->advance - gm->bearingX
1729 - (gm->bbox.xMax - gm->bbox.xMin);
1730 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1731 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1736 ascent = (ascent > casc ? ascent : casc );
1737 descent = (descent < cdesc ? descent : cdesc);
1741 cords[BBOX_NEG_WIDTH]=start;
1742 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1743 cords[BBOX_POS_WIDTH]=width - rightb / 64;
1744 cords[BBOX_GLOBAL_ASCENT]=gascent;
1745 cords[BBOX_DESCENT]=descent;
1746 cords[BBOX_ASCENT]=ascent;
1747 cords[BBOX_ADVANCE_WIDTH] = width;
1749 return BBOX_ADVANCE_WIDTH + 1;
1754 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1756 Interface to get a strings bounding box
1758 handle - pointer to font handle
1759 points - font size to use
1760 txt - string to render
1761 len - length of the string to render
1762 cords - the bounding box (modified in place)
1768 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1772 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1774 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1775 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1776 mm_log((1,"i_tt_text: get instance failed\n"));
1780 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1784 =item i_tt_face_name(handle, name_buf, name_buf_size)
1786 Retrieve's the font's postscript name.
1788 This is complicated by the need to handle encodings and so on.
1793 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1794 TT_Face_Properties props;
1797 TT_UShort platform_id, encoding_id, lang_id, name_id;
1800 int want_index = -1; /* an acceptable but not perfect name */
1805 TT_Get_Face_Properties(handle->face, &props);
1806 name_count = props.num_Names;
1807 for (i = 0; i < name_count; ++i) {
1808 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1811 TT_Get_Name_String(handle->face, i, &name, &name_len);
1813 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1814 && name_id == TT_NAME_ID_PS_NAME) {
1815 int might_want_index = -1;
1816 int might_score = 0;
1817 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1819 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1820 /* exactly what we want */
1825 if (platform_id == TT_PLATFORM_MICROSOFT
1826 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1827 /* any english is good */
1828 might_want_index = i;
1831 /* there might be something in between */
1833 /* anything non-unicode is better than nothing */
1834 might_want_index = i;
1837 if (might_score > score) {
1838 score = might_score;
1839 want_index = might_want_index;
1844 if (want_index != -1) {
1845 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1847 strncpy(name_buf, name, name_buf_size);
1848 name_buf[name_buf_size-1] = '\0';
1850 return strlen(name) + 1;
1853 i_push_error(0, "no face name present");
1858 void i_tt_dump_names(TT_Fonthandle *handle) {
1859 TT_Face_Properties props;
1862 TT_UShort platform_id, encoding_id, lang_id, name_id;
1866 TT_Get_Face_Properties(handle->face, &props);
1867 name_count = props.num_Names;
1868 for (i = 0; i < name_count; ++i) {
1869 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1871 TT_Get_Name_String(handle->face, i, &name, &name_len);
1873 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1874 encoding_id, lang_id, name_id);
1875 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1876 printf("(unicode)\n");
1879 printf("'%s'\n", name);
1885 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1886 size_t name_buf_size) {
1894 if (!handle->loaded_names) {
1896 mm_log((1, "Loading PS Names"));
1897 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1898 ++handle->loaded_names;
1901 if (handle->load_cond) {
1902 i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1906 index = TT_Char_Index(handle->char_map, ch);
1908 i_push_error(0, "no such character");
1912 rc = TT_Get_PS_Name(handle->face, index, &psname);
1915 i_push_error(rc, "error getting name");
1919 strncpy(name_buf, psname, name_buf_size);
1920 name_buf[name_buf_size-1] = '\0';
1922 return strlen(psname) + 1;
1924 mm_log((1, "FTXPOST extension not enabled\n"));
1926 i_push_error(0, "Use of FTXPOST extension disabled");
1932 #endif /* HAVE_LIBTT */
1940 Arnar M. Hrafnkelsson <addi@umich.edu>