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)) {
492 c = i_utf8_advance(&text, &len);
494 i_push_error(0, "invalid UTF8 character");
499 c = (unsigned char)*text++;
504 /* limit of 256 characters for T1 */
508 char const * name = T1_GetCharName(font_num, (unsigned char)c);
511 *out++ = strcmp(name, ".notdef") != 0;
514 mm_log((2, " No name found for character %lx\n", c));
525 =item i_t1_face_name(font_num, name_buf, name_buf_size)
527 Copies the face name of the given C<font_num> to C<name_buf>. Returns
528 the number of characters required to store the name (which can be
529 larger than C<name_buf_size>, including the space required to store
530 the terminating NUL).
532 If name_buf is too small (as specified by name_buf_size) then the name
533 will be truncated. name_buf will always be NUL termintaed.
539 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
543 if (T1_LoadFont(font_num)) {
547 name = T1_GetFontName(font_num);
550 strncpy(name_buf, name, name_buf_size);
551 name_buf[name_buf_size-1] = '\0';
552 return strlen(name) + 1;
561 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
562 size_t name_buf_size) {
569 if (T1_LoadFont(font_num)) {
573 name = T1_GetCharName(font_num, (unsigned char)ch);
575 if (strcmp(name, ".notdef")) {
576 strncpy(name_buf, name, name_buf_size);
577 name_buf[name_buf_size-1] = '\0';
578 return strlen(name) + 1;
591 t1_push_error(void) {
594 i_push_error(0, "No error");
597 #ifdef T1ERR_SCAN_FONT_FORMAT
598 case T1ERR_SCAN_FONT_FORMAT:
599 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
603 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
604 case T1ERR_SCAN_FILE_OPEN_ERR:
605 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
609 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
610 case T1ERR_SCAN_OUT_OF_MEMORY:
611 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
615 #ifdef T1ERR_SCAN_ERROR
616 case T1ERR_SCAN_ERROR:
617 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
621 #ifdef T1ERR_SCAN_FILE_EOF
622 case T1ERR_SCAN_FILE_EOF:
623 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
627 #ifdef T1ERR_PATH_ERROR
628 case T1ERR_PATH_ERROR:
629 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
633 #ifdef T1ERR_PARSE_ERROR
634 case T1ERR_PARSE_ERROR:
635 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
639 #ifdef T1ERR_TYPE1_ABORT
640 case T1ERR_TYPE1_ABORT:
641 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
645 #ifdef T1ERR_INVALID_FONTID
646 case T1ERR_INVALID_FONTID:
647 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
651 #ifdef T1ERR_INVALID_PARAMETER
652 case T1ERR_INVALID_PARAMETER:
653 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
657 #ifdef T1ERR_OP_NOT_PERMITTED
658 case T1ERR_OP_NOT_PERMITTED:
659 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
663 #ifdef T1ERR_ALLOC_MEM
664 case T1ERR_ALLOC_MEM:
665 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
669 #ifdef T1ERR_FILE_OPEN_ERR
670 case T1ERR_FILE_OPEN_ERR:
671 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
675 #ifdef T1ERR_UNSPECIFIED
676 case T1ERR_UNSPECIFIED:
677 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
681 #ifdef T1ERR_NO_AFM_DATA
682 case T1ERR_NO_AFM_DATA:
683 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
689 i_push_error(T1ERR_X11, "X11");
693 #ifdef T1ERR_COMPOSITE_CHAR
694 case T1ERR_COMPOSITE_CHAR:
695 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
700 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
704 #endif /* HAVE_LIBT1 */
707 /* Truetype font support */
710 /* This is enabled by default when configuring Freetype 1.x
711 I haven't a clue how to reliably detect it at compile time.
713 We need a compilation probe in Makefile.PL
717 #include <freetype.h>
724 /* some versions of FT1.x don't seem to define this - it's font defined
725 so it won't change */
726 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
727 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
730 /* convert a code point into an index in the glyph cache */
731 #define TT_HASH(x) ((x) & 0xFF)
733 typedef struct i_glyph_entry_ {
738 #define TT_NOCHAR (~0UL)
740 struct TT_Instancehandle_ {
741 TT_Instance instance;
742 TT_Instance_Metrics imetrics;
743 TT_Glyph_Metrics gmetrics[256];
744 i_tt_glyph_entry glyphs[256];
750 typedef struct TT_Instancehandle_ TT_Instancehandle;
752 struct TT_Fonthandle_ {
754 TT_Face_Properties properties;
755 TT_Instancehandle instanceh[TT_CHC];
765 #define USTRCT(x) ((x).z)
766 #define TT_VALID( handle ) ( ( handle ).z != NULL )
771 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
772 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
773 static void i_tt_done_raster_map( TT_Raster_Map *bit );
774 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
775 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
776 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
778 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
779 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
780 int x_off, int y_off, int smooth );
782 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
783 TT_Raster_Map *small_bit, int cords[6],
784 char const* txt, int len, int smooth, int utf8 );
785 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
786 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
788 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
789 float points, char const* txt, int len, int smooth, int utf8 );
790 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
793 /* static globals needed */
795 static TT_Engine engine;
796 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
797 static int LTT_hinted = 1; /* FIXME: this too */
808 Initializes the freetype font rendering engine
816 mm_log((1,"init_tt()\n"));
817 error = TT_Init_FreeType( &engine );
819 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
824 error = TT_Init_Post_Extension( engine );
826 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
836 =item i_tt_get_instance(handle, points, smooth)
838 Finds a points+smooth instance or if one doesn't exist in the cache
839 allocates room and returns its cache entry
841 fontname - path to the font to load
842 handle - handle to the font.
843 points - points of the requested font
844 smooth - boolean (True: antialias on, False: antialias is off)
851 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
855 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
856 handle,points,smooth));
858 if (smooth == -1) { /* Smooth doesn't matter for this search */
859 for(i=0;i<TT_CHC;i++) {
860 if (handle->instanceh[i].ptsize==points) {
861 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
865 smooth=1; /* We will be adding a font - add it as smooth then */
866 } else { /* Smooth doesn't matter for this search */
867 for(i=0;i<TT_CHC;i++) {
868 if (handle->instanceh[i].ptsize == points
869 && handle->instanceh[i].smooth == smooth) {
870 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
876 /* Found the instance in the cache - return the cache index */
878 for(idx=0;idx<TT_CHC;idx++) {
879 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
882 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
883 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
884 USTRCT(handle->instanceh[idx].instance) ));
886 if ( USTRCT(handle->instanceh[idx].instance) ) {
887 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
889 /* Free cached glyphs */
891 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
892 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
895 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
896 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
899 /* Free instance if needed */
900 TT_Done_Instance( handle->instanceh[idx].instance );
903 /* create and initialize instance */
904 /* FIXME: probably a memory leak on fail */
906 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
907 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
908 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
911 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
915 /* Now that the instance should the inplace we need to lower all of the
916 ru counts and put `this' one with the highest entry */
918 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
920 handle->instanceh[idx].order=TT_CHC-1;
921 handle->instanceh[idx].ptsize=points;
922 handle->instanceh[idx].smooth=smooth;
923 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
925 /* Zero the memory for the glyph storage so they are not thought as
926 cached if they haven't been cached since this new font was loaded */
929 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
930 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
938 =item i_tt_new(fontname)
940 Creates a new font handle object, finds a character map and initialise the
941 the font handle's cache
943 fontname - path to the font to load
949 i_tt_new(char *fontname) {
951 TT_Fonthandle *handle;
953 unsigned short platform,encoding;
955 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
957 /* allocate memory for the structure */
959 handle = mymalloc( sizeof(TT_Fonthandle) );
961 /* load the typeface */
962 error = TT_Open_Face( engine, fontname, &handle->face );
964 if ( error == TT_Err_Could_Not_Open_File ) {
965 mm_log((1, "Could not find/open %s.\n", fontname ));
968 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
974 TT_Get_Face_Properties( handle->face, &(handle->properties) );
976 /* First, look for a Unicode charmap */
977 n = handle->properties.num_CharMaps;
978 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
980 for ( i = 0; i < n; i++ ) {
981 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
982 if ( (platform == 3 && encoding == 1 )
983 || (platform == 0 && encoding == 0 ) ) {
984 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
985 platform, encoding));
986 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
990 if (!USTRCT(handle->char_map) && n != 0) {
991 /* just use the first one */
992 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
995 /* Zero the pointsizes - and ordering */
997 for(i=0;i<TT_CHC;i++) {
998 USTRCT(handle->instanceh[i].instance)=NULL;
999 handle->instanceh[i].order=i;
1000 handle->instanceh[i].ptsize=0;
1001 handle->instanceh[i].smooth=-1;
1005 handle->loaded_names = 0;
1008 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1015 * raster map management
1019 =item i_tt_init_raster_map(bit, width, height, smooth)
1021 Allocates internal memory for the bitmap as needed by the parameters (internal)
1023 bit - bitmap to allocate into
1024 width - width of the bitmap
1025 height - height of the bitmap
1026 smooth - boolean (True: antialias on, False: antialias is off)
1033 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1035 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1038 bit->width = ( width + 3 ) & -4;
1039 bit->flow = TT_Flow_Down;
1042 bit->cols = bit->width;
1043 bit->size = bit->rows * bit->width;
1045 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1046 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1049 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 ));
1051 bit->bitmap = (void *) mymalloc( bit->size );
1052 if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1057 =item i_tt_clear_raster_map(bit)
1059 Frees the bitmap data and sets pointer to NULL (internal)
1061 bit - bitmap to free
1068 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1069 myfree( bit->bitmap );
1075 =item i_tt_clear_raster_map(bit)
1077 Clears the specified bitmap (internal)
1079 bit - bitmap to zero
1087 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1088 memset( bit->bitmap, 0, bit->size );
1093 =item i_tt_blit_or(dst, src, x_off, y_off)
1095 function that blits one raster map into another (internal)
1097 dst - destination bitmap
1099 x_off - x offset into the destination bitmap
1100 y_off - y offset into the destination bitmap
1107 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1112 x1 = x_off < 0 ? -x_off : 0;
1113 y1 = y_off < 0 ? -y_off : 0;
1115 x2 = (int)dst->cols - x_off;
1116 if ( x2 > src->cols ) x2 = src->cols;
1118 y2 = (int)dst->rows - y_off;
1119 if ( y2 > src->rows ) y2 = src->rows;
1121 if ( x1 >= x2 ) return;
1123 /* do the real work now */
1125 for ( y = y1; y < y2; ++y ) {
1126 s = ( (char*)src->bitmap ) + y * src->cols + x1;
1127 d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1129 for ( x = x1; x < x2; ++x ) {
1138 /* useful for debugging */
1141 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1143 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1144 for (y = 0; y < bit->rows; ++y) {
1145 fprintf(out, "%2d:", y);
1146 for (x = 0; x < bit->cols; ++x) {
1147 if ((x & 7) == 0 && x) putc(' ', out);
1148 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1157 =item i_tt_get_glyph(handle, inst, j)
1159 Function to see if a glyph exists and if so cache it (internal)
1161 handle - pointer to font handle
1162 inst - font instance
1163 j - charcode of glyph
1170 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1171 unsigned short load_flags, code;
1174 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1175 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1177 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1179 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1180 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1181 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1185 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1186 /* clean up the entry */
1187 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1188 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1189 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1192 /* Ok - it wasn't cached - try to get it in */
1193 load_flags = TTLOAD_SCALE_GLYPH;
1194 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1196 if ( !TT_VALID(handle->char_map) ) {
1197 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1198 if ( code >= handle->properties.num_Glyphs ) code = 0;
1199 } else code = TT_Char_Index( handle->char_map, j );
1201 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1202 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1203 i_push_error(error, "TT_New_Glyph()");
1206 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1207 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1209 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1210 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1211 i_push_error(error, "TT_Load_Glyph()");
1215 /* At this point the glyph should be allocated and loaded */
1216 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1218 /* Next get the glyph metrics */
1219 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1220 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1222 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1223 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1224 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1225 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1226 i_push_error(error, "TT_Get_Glyph_Metrics()");
1234 =item i_tt_has_chars(handle, text, len, utf8, out)
1236 Check if the given characters are defined by the font. Note that len
1237 is the number of bytes, not the number of characters (when utf8 is
1240 Returns the number of characters that were checked.
1246 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1249 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1250 handle, text, len, utf8));
1256 c = i_utf8_advance(&text, &len);
1258 i_push_error(0, "invalid UTF8 character");
1263 c = (unsigned char)*text++;
1267 if (TT_VALID(handle->char_map)) {
1268 index = TT_Char_Index(handle->char_map, c);
1271 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1272 if (index >= handle->properties.num_Glyphs)
1275 *out++ = index != 0;
1283 =item i_tt_destroy(handle)
1285 Clears the data taken by a font including all cached data such as
1288 handle - pointer to font handle
1294 i_tt_destroy( TT_Fonthandle *handle) {
1295 TT_Close_Face( handle->face );
1298 /* FIXME: Should these be freed automatically by the library?
1300 TT_Done_Instance( instance );
1302 i_tt_done_glyphs( void ) {
1305 if ( !glyphs ) return;
1307 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1317 * FreeType Rendering functions
1322 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1324 Renders a single glyph into the bit rastermap (internal)
1326 handle - pointer to font handle
1327 gmetrics - the metrics for the glyph to be rendered
1328 bit - large bitmap that is the destination for the text
1329 smallbit - small bitmap that is used only if smooth is true
1330 x_off - x offset of glyph
1331 y_off - y offset of glyph
1332 smooth - boolean (True: antialias on, False: antialias is off)
1339 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 ) {
1341 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",
1342 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1344 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1346 TT_F26Dot6 xmin, ymin, xmax, ymax;
1348 xmin = gmetrics->bbox.xMin & -64;
1349 ymin = gmetrics->bbox.yMin & -64;
1350 xmax = (gmetrics->bbox.xMax + 63) & -64;
1351 ymax = (gmetrics->bbox.yMax + 63) & -64;
1353 i_tt_clear_raster_map( small_bit );
1354 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1355 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1361 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1363 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1365 handle - pointer to font handle
1366 inst - font instance
1367 bit - large bitmap that is the destination for the text
1368 smallbit - small bitmap that is used only if smooth is true
1369 txt - string to render
1370 len - length of the string to render
1371 smooth - boolean (True: antialias on, False: antialias is off)
1378 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1379 TT_Raster_Map *small_bit, int cords[6],
1380 char const* txt, int len, int smooth, int utf8 ) {
1384 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",
1385 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1388 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1391 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 */
1396 j = i_utf8_advance(&txt, &len);
1398 i_push_error(0, "invalid UTF8 character");
1403 j = (unsigned char)*txt++;
1406 if ( !i_tt_get_glyph(handle,inst,j) )
1408 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1409 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1410 small_bit, x, y, smooth );
1411 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1419 * Functions to render rasters (single channel images) onto images
1423 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1425 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1427 im - image to dump raster on
1428 bit - bitmap that contains the text to be dumped to im
1429 xb, yb - coordinates, left edge and baseline
1430 cl - color to use for text
1431 smooth - boolean (True: antialias on, False: antialias is off)
1438 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1442 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));
1444 bmap = (char *)bit->bitmap;
1448 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1449 c=(255*bmap[y*(bit->cols)+x])/4;
1451 i_gpix(im,x+xb,y+yb,&val);
1452 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1453 i_ppix(im,x+xb,y+yb,&val);
1458 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1459 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1461 i_gpix(im,x+xb,y+yb,&val);
1462 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1463 i_ppix(im,x+xb,y+yb,&val);
1471 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1473 Function to dump a raster onto a single channel image in color (internal)
1475 im - image to dump raster on
1476 bit - bitmap that contains the text to be dumped to im
1477 xb, yb - coordinates, left edge and baseline
1478 channel - channel to copy to
1479 smooth - boolean (True: antialias on, False: antialias is off)
1486 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1491 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));
1493 bmap = (char *)bit->bitmap;
1496 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1497 c=(255*bmap[y*(bit->cols)+x])/4;
1498 i_gpix(im,x+xb,y+yb,&val);
1499 val.channel[channel]=c;
1500 i_ppix(im,x+xb,y+yb,&val);
1503 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1504 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1505 i_gpix(im,x+xb,y+yb,&val);
1506 val.channel[channel]=c;
1507 i_ppix(im,x+xb,y+yb,&val);
1514 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1516 interface for generating single channel raster of text (internal)
1518 handle - pointer to font handle
1519 bit - the bitmap that is allocated, rendered into and NOT freed
1520 cords - the bounding box (modified in place)
1521 points - font size to use
1522 txt - string to render
1523 len - length of the string to render
1524 smooth - boolean (True: antialias on, False: antialias is off)
1531 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1534 TT_Raster_Map small_bit;
1536 /* find or install an instance */
1537 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1538 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1542 /* calculate bounding box */
1543 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1547 width = cords[2]-cords[0];
1548 height = cords[5]-cords[4];
1550 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1552 i_tt_init_raster_map ( bit, width, height, smooth );
1553 i_tt_clear_raster_map( bit );
1554 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1556 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1559 i_tt_done_raster_map( &small_bit );
1563 /* ascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */
1565 if ( smooth ) i_tt_done_raster_map( &small_bit );
1572 * Exported text rendering interfaces
1577 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1579 Interface to text rendering into a single channel in an image
1581 handle - pointer to font handle
1582 im - image to render text on to
1583 xb, yb - coordinates, left edge and baseline
1584 channel - channel to render into
1585 points - font size to use
1586 txt - string to render
1587 len - length of the string to render
1588 smooth - boolean (True: antialias on, False: antialias is off)
1594 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 ) {
1596 int cords[BOUNDING_BOX_COUNT];
1597 int ascent, st_offset;
1601 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1606 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth );
1607 i_tt_done_raster_map( &bit );
1614 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1616 Interface to text rendering in a single color onto an image
1618 handle - pointer to font handle
1619 im - image to render text on to
1620 xb, yb - coordinates, left edge and baseline
1621 cl - color to use for text
1622 points - font size to use
1623 txt - string to render
1624 len - length of the string to render
1625 smooth - boolean (True: antialias on, False: antialias is off)
1631 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) {
1632 int cords[BOUNDING_BOX_COUNT];
1633 int ascent, st_offset;
1638 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1643 i_tt_dump_raster_map2( im, &bit, xb+st_offset, yb-ascent, cl, smooth );
1644 i_tt_done_raster_map( &bit );
1651 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1653 Function to get texts bounding boxes given the instance of the font (internal)
1655 handle - pointer to font handle
1656 inst - font instance
1657 txt - string to measure
1658 len - length of the string to render
1659 cords - the bounding box (modified in place)
1666 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1667 int i, upm, casc, cdesc, first;
1678 unsigned char *ustr;
1679 ustr=(unsigned char*)txt;
1681 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));
1683 upm = handle->properties.header->Units_Per_EM;
1684 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1685 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1690 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1695 j = i_utf8_advance(&txt, &len);
1697 i_push_error(0, "invalid UTF8 character");
1702 j = (unsigned char)*txt++;
1705 if ( i_tt_get_glyph(handle,inst,j) ) {
1706 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1707 width += gm->advance / 64;
1708 casc = (gm->bbox.yMax+63) / 64;
1709 cdesc = (gm->bbox.yMin-63) / 64;
1711 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1712 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1715 start = gm->bbox.xMin / 64;
1716 ascent = (gm->bbox.yMax+63) / 64;
1717 descent = (gm->bbox.yMin-63) / 64;
1721 /* the right-side bearing - in case the right-side of a
1722 character goes past the right of the advance width,
1723 as is common for italic fonts
1725 rightb = gm->advance - gm->bearingX
1726 - (gm->bbox.xMax - gm->bbox.xMin);
1727 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1728 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1733 ascent = (ascent > casc ? ascent : casc );
1734 descent = (descent < cdesc ? descent : cdesc);
1738 cords[BBOX_NEG_WIDTH]=start;
1739 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1740 cords[BBOX_POS_WIDTH]=width - rightb / 64;
1741 cords[BBOX_GLOBAL_ASCENT]=gascent;
1742 cords[BBOX_DESCENT]=descent;
1743 cords[BBOX_ASCENT]=ascent;
1744 cords[BBOX_ADVANCE_WIDTH] = width;
1746 return BBOX_ADVANCE_WIDTH + 1;
1751 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1753 Interface to get a strings bounding box
1755 handle - pointer to font handle
1756 points - font size to use
1757 txt - string to render
1758 len - length of the string to render
1759 cords - the bounding box (modified in place)
1765 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1769 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1771 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1772 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1773 mm_log((1,"i_tt_text: get instance failed\n"));
1777 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1781 =item i_tt_face_name(handle, name_buf, name_buf_size)
1783 Retrieve's the font's postscript name.
1785 This is complicated by the need to handle encodings and so on.
1790 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1791 TT_Face_Properties props;
1794 TT_UShort platform_id, encoding_id, lang_id, name_id;
1797 int want_index = -1; /* an acceptable but not perfect name */
1802 TT_Get_Face_Properties(handle->face, &props);
1803 name_count = props.num_Names;
1804 for (i = 0; i < name_count; ++i) {
1805 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1808 TT_Get_Name_String(handle->face, i, &name, &name_len);
1810 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1811 && name_id == TT_NAME_ID_PS_NAME) {
1812 int might_want_index = -1;
1813 int might_score = 0;
1814 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1816 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1817 /* exactly what we want */
1822 if (platform_id == TT_PLATFORM_MICROSOFT
1823 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1824 /* any english is good */
1825 might_want_index = i;
1828 /* there might be something in between */
1830 /* anything non-unicode is better than nothing */
1831 might_want_index = i;
1834 if (might_score > score) {
1835 score = might_score;
1836 want_index = might_want_index;
1841 if (want_index != -1) {
1842 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1844 strncpy(name_buf, name, name_buf_size);
1845 name_buf[name_buf_size-1] = '\0';
1847 return strlen(name) + 1;
1850 i_push_error(0, "no face name present");
1855 void i_tt_dump_names(TT_Fonthandle *handle) {
1856 TT_Face_Properties props;
1859 TT_UShort platform_id, encoding_id, lang_id, name_id;
1863 TT_Get_Face_Properties(handle->face, &props);
1864 name_count = props.num_Names;
1865 for (i = 0; i < name_count; ++i) {
1866 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1868 TT_Get_Name_String(handle->face, i, &name, &name_len);
1870 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1871 encoding_id, lang_id, name_id);
1872 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1873 printf("(unicode)\n");
1876 printf("'%s'\n", name);
1882 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1883 size_t name_buf_size) {
1891 if (!handle->loaded_names) {
1893 mm_log((1, "Loading PS Names"));
1894 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1895 ++handle->loaded_names;
1898 if (handle->load_cond) {
1899 i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1903 index = TT_Char_Index(handle->char_map, ch);
1905 i_push_error(0, "no such character");
1909 rc = TT_Get_PS_Name(handle->face, index, &psname);
1912 i_push_error(rc, "error getting name");
1916 strncpy(name_buf, psname, name_buf_size);
1917 name_buf[name_buf_size-1] = '\0';
1919 return strlen(psname) + 1;
1921 mm_log((1, "FTXPOST extension not enabled\n"));
1923 i_push_error(0, "Use of FTXPOST extension disabled");
1929 #endif /* HAVE_LIBTT */
1937 Arnar M. Hrafnkelsson <addi@umich.edu>