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;
331 cords[BBOX_RIGHT_BEARING] =
332 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
334 return BBOX_RIGHT_BEARING+1;
339 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
341 Interface to text rendering in a single color onto an image
343 im - pointer to image structure
344 xb - x coordinate of start of string
345 yb - y coordinate of start of string ( see align )
346 cl - color to draw the text in
347 fontnum - t1 library font id
348 points - number of points in fontheight
349 str - char pointer to string to render
351 align - (0 - top of font glyph | 1 - baseline )
357 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) {
359 int xsize,ysize,x,y,ch;
362 int mod_flags = t1_get_flags(flags);
364 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
368 char *work = t1_from_utf8(str, len, &worklen);
369 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
373 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
378 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
379 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
380 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
381 mm_log((1,"bpp: %d\n",glyph->bpp));
383 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
384 ysize=glyph->metrics.ascent-glyph->metrics.descent;
386 mm_log((1,"width: %d height: %d\n",xsize,ysize));
388 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
390 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
391 c=glyph->bits[y*xsize+x];
393 i_gpix(im,x+xb,y+yb,&val);
394 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
395 i_ppix(im,x+xb,y+yb,&val);
401 =item t1_get_flags(flags)
403 Processes the characters in I<flags> to create a mod_flags value used
404 by some T1Lib functions.
409 t1_get_flags(char const *flags) {
410 int mod_flags = T1_KERNING;
414 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
415 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
416 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
417 /* ignore anything we don't recognize */
425 =item t1_from_utf8(char const *in, int len, int *outlen)
427 Produces an unencoded version of I<in> by dropping any Unicode
430 Returns a newly allocated buffer which should be freed with myfree().
431 Sets *outlen to the number of bytes used in the output string.
437 t1_from_utf8(char const *in, int len, int *outlen) {
438 char *out = mymalloc(len+1);
443 c = i_utf8_advance(&in, &len);
446 i_push_error(0, "invalid UTF8 character");
449 /* yeah, just drop them */
461 =item i_t1_has_chars(font_num, text, len, utf8, out)
463 Check if the given characters are defined by the font. Note that len
464 is the number of bytes, not the number of characters (when utf8 is
467 out[char index] will be true if the character exists.
469 Accepts UTF-8, but since T1 can only have 256 characters, any chars
470 with values over 255 will simply be returned as false.
472 Returns the number of characters that were checked.
478 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
482 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
483 font_num, text, len, utf8));
486 if (T1_LoadFont(font_num)) {
494 c = i_utf8_advance(&text, &len);
496 i_push_error(0, "invalid UTF8 character");
501 c = (unsigned char)*text++;
506 /* limit of 256 characters for T1 */
510 char const * name = T1_GetCharName(font_num, (unsigned char)c);
513 *out++ = strcmp(name, ".notdef") != 0;
516 mm_log((2, " No name found for character %lx\n", c));
527 =item i_t1_face_name(font_num, name_buf, name_buf_size)
529 Copies the face name of the given C<font_num> to C<name_buf>. Returns
530 the number of characters required to store the name (which can be
531 larger than C<name_buf_size>, including the space required to store
532 the terminating NUL).
534 If name_buf is too small (as specified by name_buf_size) then the name
535 will be truncated. name_buf will always be NUL termintaed.
541 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
545 if (T1_LoadFont(font_num)) {
549 name = T1_GetFontName(font_num);
552 strncpy(name_buf, name, name_buf_size);
553 name_buf[name_buf_size-1] = '\0';
554 return strlen(name) + 1;
563 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
564 size_t name_buf_size) {
571 if (T1_LoadFont(font_num)) {
575 name = T1_GetCharName(font_num, (unsigned char)ch);
577 if (strcmp(name, ".notdef")) {
578 strncpy(name_buf, name, name_buf_size);
579 name_buf[name_buf_size-1] = '\0';
580 return strlen(name) + 1;
593 t1_push_error(void) {
596 i_push_error(0, "No error");
599 #ifdef T1ERR_SCAN_FONT_FORMAT
600 case T1ERR_SCAN_FONT_FORMAT:
601 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
605 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
606 case T1ERR_SCAN_FILE_OPEN_ERR:
607 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
611 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
612 case T1ERR_SCAN_OUT_OF_MEMORY:
613 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
617 #ifdef T1ERR_SCAN_ERROR
618 case T1ERR_SCAN_ERROR:
619 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
623 #ifdef T1ERR_SCAN_FILE_EOF
624 case T1ERR_SCAN_FILE_EOF:
625 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
629 #ifdef T1ERR_PATH_ERROR
630 case T1ERR_PATH_ERROR:
631 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
635 #ifdef T1ERR_PARSE_ERROR
636 case T1ERR_PARSE_ERROR:
637 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
641 #ifdef T1ERR_TYPE1_ABORT
642 case T1ERR_TYPE1_ABORT:
643 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
647 #ifdef T1ERR_INVALID_FONTID
648 case T1ERR_INVALID_FONTID:
649 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
653 #ifdef T1ERR_INVALID_PARAMETER
654 case T1ERR_INVALID_PARAMETER:
655 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
659 #ifdef T1ERR_OP_NOT_PERMITTED
660 case T1ERR_OP_NOT_PERMITTED:
661 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
665 #ifdef T1ERR_ALLOC_MEM
666 case T1ERR_ALLOC_MEM:
667 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
671 #ifdef T1ERR_FILE_OPEN_ERR
672 case T1ERR_FILE_OPEN_ERR:
673 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
677 #ifdef T1ERR_UNSPECIFIED
678 case T1ERR_UNSPECIFIED:
679 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
683 #ifdef T1ERR_NO_AFM_DATA
684 case T1ERR_NO_AFM_DATA:
685 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
691 i_push_error(T1ERR_X11, "X11");
695 #ifdef T1ERR_COMPOSITE_CHAR
696 case T1ERR_COMPOSITE_CHAR:
697 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
702 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
706 #endif /* HAVE_LIBT1 */
709 /* Truetype font support */
712 /* This is enabled by default when configuring Freetype 1.x
713 I haven't a clue how to reliably detect it at compile time.
715 We need a compilation probe in Makefile.PL
719 #include <freetype.h>
726 /* some versions of FT1.x don't seem to define this - it's font defined
727 so it won't change */
728 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
729 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
732 /* convert a code point into an index in the glyph cache */
733 #define TT_HASH(x) ((x) & 0xFF)
735 typedef struct i_glyph_entry_ {
740 #define TT_NOCHAR (~0UL)
742 struct TT_Instancehandle_ {
743 TT_Instance instance;
744 TT_Instance_Metrics imetrics;
745 TT_Glyph_Metrics gmetrics[256];
746 i_tt_glyph_entry glyphs[256];
752 typedef struct TT_Instancehandle_ TT_Instancehandle;
754 struct TT_Fonthandle_ {
756 TT_Face_Properties properties;
757 TT_Instancehandle instanceh[TT_CHC];
767 #define USTRCT(x) ((x).z)
768 #define TT_VALID( handle ) ( ( handle ).z != NULL )
773 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
774 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
775 static void i_tt_done_raster_map( TT_Raster_Map *bit );
776 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
777 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
778 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
780 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
781 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
782 int x_off, int y_off, int smooth );
784 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
785 TT_Raster_Map *small_bit, int cords[6],
786 char const* txt, int len, int smooth, int utf8 );
787 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
788 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
790 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
791 float points, char const* txt, int len, int smooth, int utf8 );
792 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
795 /* static globals needed */
797 static TT_Engine engine;
798 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
799 static int LTT_hinted = 1; /* FIXME: this too */
810 Initializes the freetype font rendering engine
818 mm_log((1,"init_tt()\n"));
819 error = TT_Init_FreeType( &engine );
821 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
826 error = TT_Init_Post_Extension( engine );
828 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
838 =item i_tt_get_instance(handle, points, smooth)
840 Finds a points+smooth instance or if one doesn't exist in the cache
841 allocates room and returns its cache entry
843 fontname - path to the font to load
844 handle - handle to the font.
845 points - points of the requested font
846 smooth - boolean (True: antialias on, False: antialias is off)
853 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
857 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
858 handle,points,smooth));
860 if (smooth == -1) { /* Smooth doesn't matter for this search */
861 for(i=0;i<TT_CHC;i++) {
862 if (handle->instanceh[i].ptsize==points) {
863 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
867 smooth=1; /* We will be adding a font - add it as smooth then */
868 } else { /* Smooth doesn't matter for this search */
869 for(i=0;i<TT_CHC;i++) {
870 if (handle->instanceh[i].ptsize == points
871 && handle->instanceh[i].smooth == smooth) {
872 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
878 /* Found the instance in the cache - return the cache index */
880 for(idx=0;idx<TT_CHC;idx++) {
881 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
884 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
885 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
886 USTRCT(handle->instanceh[idx].instance) ));
888 if ( USTRCT(handle->instanceh[idx].instance) ) {
889 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
891 /* Free cached glyphs */
893 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
894 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
897 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
898 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
901 /* Free instance if needed */
902 TT_Done_Instance( handle->instanceh[idx].instance );
905 /* create and initialize instance */
906 /* FIXME: probably a memory leak on fail */
908 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
909 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
910 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
913 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
917 /* Now that the instance should the inplace we need to lower all of the
918 ru counts and put `this' one with the highest entry */
920 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
922 handle->instanceh[idx].order=TT_CHC-1;
923 handle->instanceh[idx].ptsize=points;
924 handle->instanceh[idx].smooth=smooth;
925 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
927 /* Zero the memory for the glyph storage so they are not thought as
928 cached if they haven't been cached since this new font was loaded */
931 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
932 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
940 =item i_tt_new(fontname)
942 Creates a new font handle object, finds a character map and initialise the
943 the font handle's cache
945 fontname - path to the font to load
951 i_tt_new(char *fontname) {
953 TT_Fonthandle *handle;
955 unsigned short platform,encoding;
957 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
959 /* allocate memory for the structure */
961 handle = mymalloc( sizeof(TT_Fonthandle) );
963 /* load the typeface */
964 error = TT_Open_Face( engine, fontname, &handle->face );
966 if ( error == TT_Err_Could_Not_Open_File ) {
967 mm_log((1, "Could not find/open %s.\n", fontname ));
970 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
976 TT_Get_Face_Properties( handle->face, &(handle->properties) );
978 /* First, look for a Unicode charmap */
979 n = handle->properties.num_CharMaps;
980 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
982 for ( i = 0; i < n; i++ ) {
983 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
984 if ( (platform == 3 && encoding == 1 )
985 || (platform == 0 && encoding == 0 ) ) {
986 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
987 platform, encoding));
988 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
992 if (!USTRCT(handle->char_map) && n != 0) {
993 /* just use the first one */
994 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
997 /* Zero the pointsizes - and ordering */
999 for(i=0;i<TT_CHC;i++) {
1000 USTRCT(handle->instanceh[i].instance)=NULL;
1001 handle->instanceh[i].order=i;
1002 handle->instanceh[i].ptsize=0;
1003 handle->instanceh[i].smooth=-1;
1007 handle->loaded_names = 0;
1010 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1017 * raster map management
1021 =item i_tt_init_raster_map(bit, width, height, smooth)
1023 Allocates internal memory for the bitmap as needed by the parameters (internal)
1025 bit - bitmap to allocate into
1026 width - width of the bitmap
1027 height - height of the bitmap
1028 smooth - boolean (True: antialias on, False: antialias is off)
1035 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1037 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1040 bit->width = ( width + 3 ) & -4;
1041 bit->flow = TT_Flow_Down;
1044 bit->cols = bit->width;
1045 bit->size = bit->rows * bit->width;
1047 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1048 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1051 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 ));
1053 bit->bitmap = (void *) mymalloc( bit->size );
1054 if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1059 =item i_tt_clear_raster_map(bit)
1061 Frees the bitmap data and sets pointer to NULL (internal)
1063 bit - bitmap to free
1070 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1071 myfree( bit->bitmap );
1077 =item i_tt_clear_raster_map(bit)
1079 Clears the specified bitmap (internal)
1081 bit - bitmap to zero
1089 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1090 memset( bit->bitmap, 0, bit->size );
1095 =item i_tt_blit_or(dst, src, x_off, y_off)
1097 function that blits one raster map into another (internal)
1099 dst - destination bitmap
1101 x_off - x offset into the destination bitmap
1102 y_off - y offset into the destination bitmap
1109 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1114 x1 = x_off < 0 ? -x_off : 0;
1115 y1 = y_off < 0 ? -y_off : 0;
1117 x2 = (int)dst->cols - x_off;
1118 if ( x2 > src->cols ) x2 = src->cols;
1120 y2 = (int)dst->rows - y_off;
1121 if ( y2 > src->rows ) y2 = src->rows;
1123 if ( x1 >= x2 ) return;
1125 /* do the real work now */
1127 for ( y = y1; y < y2; ++y ) {
1128 s = ( (char*)src->bitmap ) + y * src->cols + x1;
1129 d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1131 for ( x = x1; x < x2; ++x ) {
1140 /* useful for debugging */
1143 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1145 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1146 for (y = 0; y < bit->rows; ++y) {
1147 fprintf(out, "%2d:", y);
1148 for (x = 0; x < bit->cols; ++x) {
1149 if ((x & 7) == 0 && x) putc(' ', out);
1150 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1159 =item i_tt_get_glyph(handle, inst, j)
1161 Function to see if a glyph exists and if so cache it (internal)
1163 handle - pointer to font handle
1164 inst - font instance
1165 j - charcode of glyph
1172 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1173 unsigned short load_flags, code;
1176 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1177 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1179 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1181 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1182 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1183 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1187 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1188 /* clean up the entry */
1189 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1190 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1191 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1194 /* Ok - it wasn't cached - try to get it in */
1195 load_flags = TTLOAD_SCALE_GLYPH;
1196 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1198 if ( !TT_VALID(handle->char_map) ) {
1199 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1200 if ( code >= handle->properties.num_Glyphs ) code = 0;
1201 } else code = TT_Char_Index( handle->char_map, j );
1203 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1204 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1205 i_push_error(error, "TT_New_Glyph()");
1208 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1209 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1211 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1212 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1213 i_push_error(error, "TT_Load_Glyph()");
1217 /* At this point the glyph should be allocated and loaded */
1218 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1220 /* Next get the glyph metrics */
1221 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1222 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1224 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1225 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1226 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1227 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1228 i_push_error(error, "TT_Get_Glyph_Metrics()");
1236 =item i_tt_has_chars(handle, text, len, utf8, out)
1238 Check if the given characters are defined by the font. Note that len
1239 is the number of bytes, not the number of characters (when utf8 is
1242 Returns the number of characters that were checked.
1248 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 ) {
1386 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",
1387 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1390 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1393 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 */
1398 j = i_utf8_advance(&txt, &len);
1400 i_push_error(0, "invalid UTF8 character");
1405 j = (unsigned char)*txt++;
1408 if ( !i_tt_get_glyph(handle,inst,j) )
1410 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1411 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1412 small_bit, x, y, smooth );
1413 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1421 * Functions to render rasters (single channel images) onto images
1425 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1427 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1429 im - image to dump raster on
1430 bit - bitmap that contains the text to be dumped to im
1431 xb, yb - coordinates, left edge and baseline
1432 cl - color to use for text
1433 smooth - boolean (True: antialias on, False: antialias is off)
1440 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1444 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));
1446 bmap = (char *)bit->bitmap;
1450 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1451 c=(255*bmap[y*(bit->cols)+x])/4;
1453 i_gpix(im,x+xb,y+yb,&val);
1454 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1455 i_ppix(im,x+xb,y+yb,&val);
1460 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1461 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1463 i_gpix(im,x+xb,y+yb,&val);
1464 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1465 i_ppix(im,x+xb,y+yb,&val);
1473 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1475 Function to dump a raster onto a single channel image in color (internal)
1477 im - image to dump raster on
1478 bit - bitmap that contains the text to be dumped to im
1479 xb, yb - coordinates, left edge and baseline
1480 channel - channel to copy to
1481 smooth - boolean (True: antialias on, False: antialias is off)
1488 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1493 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));
1495 bmap = (char *)bit->bitmap;
1498 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1499 c=(255*bmap[y*(bit->cols)+x])/4;
1500 i_gpix(im,x+xb,y+yb,&val);
1501 val.channel[channel]=c;
1502 i_ppix(im,x+xb,y+yb,&val);
1505 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1506 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1507 i_gpix(im,x+xb,y+yb,&val);
1508 val.channel[channel]=c;
1509 i_ppix(im,x+xb,y+yb,&val);
1516 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1518 interface for generating single channel raster of text (internal)
1520 handle - pointer to font handle
1521 bit - the bitmap that is allocated, rendered into and NOT freed
1522 cords - the bounding box (modified in place)
1523 points - font size to use
1524 txt - string to render
1525 len - length of the string to render
1526 smooth - boolean (True: antialias on, False: antialias is off)
1533 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1536 TT_Raster_Map small_bit;
1538 /* find or install an instance */
1539 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1540 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1544 /* calculate bounding box */
1545 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1549 width = cords[2]-cords[0];
1550 height = cords[5]-cords[4];
1552 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1554 i_tt_init_raster_map ( bit, width, height, smooth );
1555 i_tt_clear_raster_map( bit );
1556 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1558 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1561 i_tt_done_raster_map( &small_bit );
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, int align ) {
1596 int cords[BOUNDING_BOX_COUNT];
1597 int ascent, st_offset, y;
1601 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1603 ascent=cords[BBOX_ASCENT];
1604 st_offset=cords[BBOX_NEG_WIDTH];
1605 y = align ? yb-ascent : yb;
1607 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1608 i_tt_done_raster_map( &bit );
1615 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1617 Interface to text rendering in a single color onto an image
1619 handle - pointer to font handle
1620 im - image to render text on to
1621 xb, yb - coordinates, left edge and baseline
1622 cl - color to use for text
1623 points - font size to use
1624 txt - string to render
1625 len - length of the string to render
1626 smooth - boolean (True: antialias on, False: antialias is off)
1632 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, int align) {
1633 int cords[BOUNDING_BOX_COUNT];
1634 int ascent, st_offset, y;
1639 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1641 ascent=cords[BBOX_ASCENT];
1642 st_offset=cords[BBOX_NEG_WIDTH];
1643 y = align ? yb-ascent : yb;
1645 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1646 i_tt_done_raster_map( &bit );
1653 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1655 Function to get texts bounding boxes given the instance of the font (internal)
1657 handle - pointer to font handle
1658 inst - font instance
1659 txt - string to measure
1660 len - length of the string to render
1661 cords - the bounding box (modified in place)
1668 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1669 int upm, casc, cdesc, first;
1680 unsigned char *ustr;
1681 ustr=(unsigned char*)txt;
1683 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));
1685 upm = handle->properties.header->Units_Per_EM;
1686 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1687 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1692 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1697 j = i_utf8_advance(&txt, &len);
1699 i_push_error(0, "invalid UTF8 character");
1704 j = (unsigned char)*txt++;
1707 if ( i_tt_get_glyph(handle,inst,j) ) {
1708 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1709 width += gm->advance / 64;
1710 casc = (gm->bbox.yMax+63) / 64;
1711 cdesc = (gm->bbox.yMin-63) / 64;
1713 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1714 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1717 start = gm->bbox.xMin / 64;
1718 ascent = (gm->bbox.yMax+63) / 64;
1719 descent = (gm->bbox.yMin-63) / 64;
1722 if (!len) { /* if at end of string */
1723 /* the right-side bearing - in case the right-side of a
1724 character goes past the right of the advance width,
1725 as is common for italic fonts
1727 rightb = gm->advance - gm->bearingX
1728 - (gm->bbox.xMax - gm->bbox.xMin);
1729 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1730 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;
1742 cords[BBOX_POS_WIDTH] -= rightb / 64;
1743 cords[BBOX_GLOBAL_ASCENT]=gascent;
1744 cords[BBOX_DESCENT]=descent;
1745 cords[BBOX_ASCENT]=ascent;
1746 cords[BBOX_ADVANCE_WIDTH] = width;
1747 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1749 return BBOX_RIGHT_BEARING + 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>