18 font.c - implements font handling functions for t1 and truetype fonts
25 fontnum = i_t1_new(path_to_pfb, path_to_afm);
26 i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
27 rc = i_t1_destroy(fontnum);
31 handle = i_tt_new(path_to_ttf);
32 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
39 font.c implements font creation, rendering, bounding box functions and
42 =head1 FUNCTION REFERENCE
44 Some of these functions are internal.
55 Initialize font rendering libraries if they are avaliable.
61 i_init_fonts(int t1log) {
62 mm_log((1,"Initializing fonts\n"));
77 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
85 static int t1_get_flags(char const *flags);
86 static char *t1_from_utf8(char const *in, int len, int *outlen);
88 static void t1_push_error(void);
91 =item i_init_t1(t1log)
93 Initializes the t1lib font rendering engine.
99 i_init_t1(int t1log) {
100 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
101 mm_log((1,"init_t1()\n"));
104 init_flags |= LOGFILE;
105 if ((T1_InitLib(init_flags) == NULL)){
106 mm_log((1,"Initialization of t1lib failed\n"));
109 T1_SetLogLevel(T1LOG_DEBUG);
110 i_t1_set_aa(1); /* Default Antialias value */
118 Shuts the t1lib font rendering engine down.
120 This it seems that this function is never used.
132 =item i_t1_new(pfb, afm)
134 Loads the fonts with the given filenames, returns its font id
136 pfb - path to pfb file for font
137 afm - path to afm file for font
143 i_t1_new(char *pfb,char *afm) {
146 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
147 font_id = T1_AddFont(pfb);
149 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
154 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
155 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
162 =item i_t1_destroy(font_id)
164 Frees resources for a t1 font with given font id.
166 font_id - number of the font to free
172 i_t1_destroy(int font_id) {
173 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
174 return T1_DeleteFont(font_id);
179 =item i_t1_set_aa(st)
181 Sets the antialiasing level of the t1 library.
183 st - 0 = NONE, 1 = LOW, 2 = HIGH.
189 i_t1_set_aa(int st) {
191 unsigned long cst[17];
194 T1_AASetBitsPerPixel( 8 );
195 T1_AASetLevel( T1_AA_NONE );
196 T1_AANSetGrayValues( 0, 255 );
197 mm_log((1,"setting T1 antialias to none\n"));
200 T1_AASetBitsPerPixel( 8 );
201 T1_AASetLevel( T1_AA_LOW );
202 T1_AASetGrayValues( 0,65,127,191,255 );
203 mm_log((1,"setting T1 antialias to low\n"));
206 T1_AASetBitsPerPixel(8);
207 T1_AASetLevel(T1_AA_HIGH);
208 for(i=0;i<17;i++) cst[i]=(i*255)/16;
209 T1_AAHSetGrayValues( cst );
210 mm_log((1,"setting T1 antialias to high\n"));
216 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
218 Interface to text rendering into a single channel in an image
220 im pointer to image structure
221 xb x coordinate of start of string
222 yb y coordinate of start of string ( see align )
223 channel - destination channel
224 fontnum - t1 library font id
225 points - number of points in fontheight
226 str - string to render
228 align - (0 - top of font glyph | 1 - baseline )
234 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) {
238 int mod_flags = t1_get_flags(flags);
240 unsigned int ch_mask_store;
242 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
246 char *work = t1_from_utf8(str, len, &worklen);
247 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
251 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
256 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
257 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
258 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
259 mm_log((1,"bpp: %d\n",glyph->bpp));
261 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
262 ysize=glyph->metrics.ascent-glyph->metrics.descent;
264 mm_log((1,"width: %d height: %d\n",xsize,ysize));
266 ch_mask_store=im->ch_mask;
267 im->ch_mask=1<<channel;
269 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
271 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
272 val.channel[channel]=glyph->bits[y*xsize+x];
273 i_ppix(im,x+xb,y+yb,&val);
276 im->ch_mask=ch_mask_store;
282 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
284 function to get a strings bounding box given the font id and sizes
286 handle - pointer to font handle
287 fontnum - t1 library font id
288 points - number of points in fontheight
289 str - string to measure
291 cords - the bounding box (modified in place)
297 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
300 int mod_flags = t1_get_flags(flags);
303 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
304 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
307 char *work = t1_from_utf8(str, len, &worklen);
308 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
312 bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
314 gbbox = T1_GetFontBBox(fontnum);
315 advance = T1_GetStringWidth(fontnum, str, len, 0, mod_flags);
317 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
318 (int)(bbox.llx*points/1000),
319 (int)(gbbox.lly*points/1000),
320 (int)(bbox.urx*points/1000),
321 (int)(gbbox.ury*points/1000),
322 (int)(bbox.lly*points/1000),
323 (int)(bbox.ury*points/1000) ));
326 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
327 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
329 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
330 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
332 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
333 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
335 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
336 cords[BBOX_RIGHT_BEARING] =
337 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
339 return BBOX_RIGHT_BEARING+1;
344 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
346 Interface to text rendering in a single color onto an image
348 im - pointer to image structure
349 xb - x coordinate of start of string
350 yb - y coordinate of start of string ( see align )
351 cl - color to draw the text in
352 fontnum - t1 library font id
353 points - number of points in fontheight
354 str - char pointer to string to render
356 align - (0 - top of font glyph | 1 - baseline )
362 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) {
364 int xsize,ysize,x,y,ch;
367 int mod_flags = t1_get_flags(flags);
369 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
373 char *work = t1_from_utf8(str, len, &worklen);
374 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
378 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
383 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
384 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
385 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
386 mm_log((1,"bpp: %d\n",glyph->bpp));
388 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
389 ysize=glyph->metrics.ascent-glyph->metrics.descent;
391 mm_log((1,"width: %d height: %d\n",xsize,ysize));
393 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
395 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
396 c=glyph->bits[y*xsize+x];
398 i_gpix(im,x+xb,y+yb,&val);
399 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
400 i_ppix(im,x+xb,y+yb,&val);
406 =item t1_get_flags(flags)
408 Processes the characters in I<flags> to create a mod_flags value used
409 by some T1Lib functions.
414 t1_get_flags(char const *flags) {
415 int mod_flags = T1_KERNING;
419 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
420 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
421 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
422 /* ignore anything we don't recognize */
430 =item t1_from_utf8(char const *in, int len, int *outlen)
432 Produces an unencoded version of I<in> by dropping any Unicode
435 Returns a newly allocated buffer which should be freed with myfree().
436 Sets *outlen to the number of bytes used in the output string.
442 t1_from_utf8(char const *in, int len, int *outlen) {
443 char *out = mymalloc(len+1);
448 c = i_utf8_advance(&in, &len);
451 i_push_error(0, "invalid UTF8 character");
454 /* yeah, just drop them */
466 =item i_t1_has_chars(font_num, text, len, utf8, out)
468 Check if the given characters are defined by the font. Note that len
469 is the number of bytes, not the number of characters (when utf8 is
472 out[char index] will be true if the character exists.
474 Accepts UTF-8, but since T1 can only have 256 characters, any chars
475 with values over 255 will simply be returned as false.
477 Returns the number of characters that were checked.
483 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
487 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
488 font_num, text, len, utf8));
491 if (T1_LoadFont(font_num)) {
499 c = i_utf8_advance(&text, &len);
501 i_push_error(0, "invalid UTF8 character");
506 c = (unsigned char)*text++;
511 /* limit of 256 characters for T1 */
515 char const * name = T1_GetCharName(font_num, (unsigned char)c);
518 *out++ = strcmp(name, ".notdef") != 0;
521 mm_log((2, " No name found for character %lx\n", c));
532 =item i_t1_face_name(font_num, name_buf, name_buf_size)
534 Copies the face name of the given C<font_num> to C<name_buf>. Returns
535 the number of characters required to store the name (which can be
536 larger than C<name_buf_size>, including the space required to store
537 the terminating NUL).
539 If name_buf is too small (as specified by name_buf_size) then the name
540 will be truncated. name_buf will always be NUL termintaed.
546 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
550 if (T1_LoadFont(font_num)) {
554 name = T1_GetFontName(font_num);
557 strncpy(name_buf, name, name_buf_size);
558 name_buf[name_buf_size-1] = '\0';
559 return strlen(name) + 1;
568 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
569 size_t name_buf_size) {
576 if (T1_LoadFont(font_num)) {
580 name = T1_GetCharName(font_num, (unsigned char)ch);
582 if (strcmp(name, ".notdef")) {
583 strncpy(name_buf, name, name_buf_size);
584 name_buf[name_buf_size-1] = '\0';
585 return strlen(name) + 1;
598 t1_push_error(void) {
601 i_push_error(0, "No error");
604 #ifdef T1ERR_SCAN_FONT_FORMAT
605 case T1ERR_SCAN_FONT_FORMAT:
606 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
610 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
611 case T1ERR_SCAN_FILE_OPEN_ERR:
612 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
616 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
617 case T1ERR_SCAN_OUT_OF_MEMORY:
618 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
622 #ifdef T1ERR_SCAN_ERROR
623 case T1ERR_SCAN_ERROR:
624 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
628 #ifdef T1ERR_SCAN_FILE_EOF
629 case T1ERR_SCAN_FILE_EOF:
630 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
634 #ifdef T1ERR_PATH_ERROR
635 case T1ERR_PATH_ERROR:
636 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
640 #ifdef T1ERR_PARSE_ERROR
641 case T1ERR_PARSE_ERROR:
642 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
646 #ifdef T1ERR_TYPE1_ABORT
647 case T1ERR_TYPE1_ABORT:
648 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
652 #ifdef T1ERR_INVALID_FONTID
653 case T1ERR_INVALID_FONTID:
654 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
658 #ifdef T1ERR_INVALID_PARAMETER
659 case T1ERR_INVALID_PARAMETER:
660 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
664 #ifdef T1ERR_OP_NOT_PERMITTED
665 case T1ERR_OP_NOT_PERMITTED:
666 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
670 #ifdef T1ERR_ALLOC_MEM
671 case T1ERR_ALLOC_MEM:
672 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
676 #ifdef T1ERR_FILE_OPEN_ERR
677 case T1ERR_FILE_OPEN_ERR:
678 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
682 #ifdef T1ERR_UNSPECIFIED
683 case T1ERR_UNSPECIFIED:
684 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
688 #ifdef T1ERR_NO_AFM_DATA
689 case T1ERR_NO_AFM_DATA:
690 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
696 i_push_error(T1ERR_X11, "X11");
700 #ifdef T1ERR_COMPOSITE_CHAR
701 case T1ERR_COMPOSITE_CHAR:
702 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
707 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
711 #endif /* HAVE_LIBT1 */
714 /* Truetype font support */
717 /* These are enabled by default when configuring Freetype 1.x
718 I haven't a clue how to reliably detect it at compile time.
720 We need a compilation probe in Makefile.PL
725 #include <freetype.h>
733 #include <ftxerr18.h>
736 /* some versions of FT1.x don't seem to define this - it's font defined
737 so it won't change */
738 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
739 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
742 /* convert a code point into an index in the glyph cache */
743 #define TT_HASH(x) ((x) & 0xFF)
745 typedef struct i_glyph_entry_ {
750 #define TT_NOCHAR (~0UL)
752 struct TT_Instancehandle_ {
753 TT_Instance instance;
754 TT_Instance_Metrics imetrics;
755 TT_Glyph_Metrics gmetrics[256];
756 i_tt_glyph_entry glyphs[256];
762 typedef struct TT_Instancehandle_ TT_Instancehandle;
764 struct TT_Fonthandle_ {
766 TT_Face_Properties properties;
767 TT_Instancehandle instanceh[TT_CHC];
777 #define USTRCT(x) ((x).z)
778 #define TT_VALID( handle ) ( ( handle ).z != NULL )
780 static void i_tt_push_error(TT_Error rc);
784 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
785 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
786 static void i_tt_done_raster_map( TT_Raster_Map *bit );
787 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
788 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
789 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
791 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
792 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
793 int x_off, int y_off, int smooth );
795 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
796 TT_Raster_Map *small_bit, int cords[6],
797 char const* txt, int len, int smooth, int utf8 );
798 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
799 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
801 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
802 float points, char const* txt, int len, int smooth, int utf8 );
803 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
806 /* static globals needed */
808 static TT_Engine engine;
809 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
810 static int LTT_hinted = 1; /* FIXME: this too */
821 Initializes the freetype font rendering engine
829 mm_log((1,"init_tt()\n"));
830 error = TT_Init_FreeType( &engine );
832 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
837 error = TT_Init_Post_Extension( engine );
839 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
849 =item i_tt_get_instance(handle, points, smooth)
851 Finds a points+smooth instance or if one doesn't exist in the cache
852 allocates room and returns its cache entry
854 fontname - path to the font to load
855 handle - handle to the font.
856 points - points of the requested font
857 smooth - boolean (True: antialias on, False: antialias is off)
864 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
868 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
869 handle,points,smooth));
871 if (smooth == -1) { /* Smooth doesn't matter for this search */
872 for(i=0;i<TT_CHC;i++) {
873 if (handle->instanceh[i].ptsize==points) {
874 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
878 smooth=1; /* We will be adding a font - add it as smooth then */
879 } else { /* Smooth doesn't matter for this search */
880 for(i=0;i<TT_CHC;i++) {
881 if (handle->instanceh[i].ptsize == points
882 && handle->instanceh[i].smooth == smooth) {
883 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
889 /* Found the instance in the cache - return the cache index */
891 for(idx=0;idx<TT_CHC;idx++) {
892 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
895 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
896 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
897 USTRCT(handle->instanceh[idx].instance) ));
899 if ( USTRCT(handle->instanceh[idx].instance) ) {
900 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
902 /* Free cached glyphs */
904 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
905 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
908 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
909 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
912 /* Free instance if needed */
913 TT_Done_Instance( handle->instanceh[idx].instance );
916 /* create and initialize instance */
917 /* FIXME: probably a memory leak on fail */
919 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
920 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
921 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
924 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
928 /* Now that the instance should the inplace we need to lower all of the
929 ru counts and put `this' one with the highest entry */
931 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
933 handle->instanceh[idx].order=TT_CHC-1;
934 handle->instanceh[idx].ptsize=points;
935 handle->instanceh[idx].smooth=smooth;
936 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
938 /* Zero the memory for the glyph storage so they are not thought as
939 cached if they haven't been cached since this new font was loaded */
942 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
943 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
951 =item i_tt_new(fontname)
953 Creates a new font handle object, finds a character map and initialise the
954 the font handle's cache
956 fontname - path to the font to load
962 i_tt_new(char *fontname) {
964 TT_Fonthandle *handle;
966 unsigned short platform,encoding;
970 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
972 /* allocate memory for the structure */
974 handle = mymalloc( sizeof(TT_Fonthandle) );
976 /* load the typeface */
977 error = TT_Open_Face( engine, fontname, &handle->face );
979 if ( error == TT_Err_Could_Not_Open_File ) {
980 mm_log((1, "Could not find/open %s.\n", fontname ));
983 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
986 i_tt_push_error(error);
990 TT_Get_Face_Properties( handle->face, &(handle->properties) );
992 /* First, look for a Unicode charmap */
993 n = handle->properties.num_CharMaps;
994 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
996 for ( i = 0; i < n; i++ ) {
997 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
998 if ( (platform == 3 && encoding == 1 )
999 || (platform == 0 && encoding == 0 ) ) {
1000 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1001 platform, encoding));
1002 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1006 if (!USTRCT(handle->char_map) && n != 0) {
1007 /* just use the first one */
1008 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1011 /* Zero the pointsizes - and ordering */
1013 for(i=0;i<TT_CHC;i++) {
1014 USTRCT(handle->instanceh[i].instance)=NULL;
1015 handle->instanceh[i].order=i;
1016 handle->instanceh[i].ptsize=0;
1017 handle->instanceh[i].smooth=-1;
1021 handle->loaded_names = 0;
1024 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1031 * raster map management
1035 =item i_tt_init_raster_map(bit, width, height, smooth)
1037 Allocates internal memory for the bitmap as needed by the parameters (internal)
1039 bit - bitmap to allocate into
1040 width - width of the bitmap
1041 height - height of the bitmap
1042 smooth - boolean (True: antialias on, False: antialias is off)
1049 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1051 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1054 bit->width = ( width + 3 ) & -4;
1055 bit->flow = TT_Flow_Down;
1058 bit->cols = bit->width;
1059 bit->size = bit->rows * bit->width;
1061 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1062 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1065 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 ));
1067 bit->bitmap = (void *) mymalloc( bit->size );
1068 if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1073 =item i_tt_clear_raster_map(bit)
1075 Frees the bitmap data and sets pointer to NULL (internal)
1077 bit - bitmap to free
1084 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1085 myfree( bit->bitmap );
1091 =item i_tt_clear_raster_map(bit)
1093 Clears the specified bitmap (internal)
1095 bit - bitmap to zero
1103 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1104 memset( bit->bitmap, 0, bit->size );
1109 =item i_tt_blit_or(dst, src, x_off, y_off)
1111 function that blits one raster map into another (internal)
1113 dst - destination bitmap
1115 x_off - x offset into the destination bitmap
1116 y_off - y offset into the destination bitmap
1123 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1128 x1 = x_off < 0 ? -x_off : 0;
1129 y1 = y_off < 0 ? -y_off : 0;
1131 x2 = (int)dst->cols - x_off;
1132 if ( x2 > src->cols ) x2 = src->cols;
1134 y2 = (int)dst->rows - y_off;
1135 if ( y2 > src->rows ) y2 = src->rows;
1137 if ( x1 >= x2 ) return;
1139 /* do the real work now */
1141 for ( y = y1; y < y2; ++y ) {
1142 s = ( (char*)src->bitmap ) + y * src->cols + x1;
1143 d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1145 for ( x = x1; x < x2; ++x ) {
1154 /* useful for debugging */
1157 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1159 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1160 for (y = 0; y < bit->rows; ++y) {
1161 fprintf(out, "%2d:", y);
1162 for (x = 0; x < bit->cols; ++x) {
1163 if ((x & 7) == 0 && x) putc(' ', out);
1164 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1173 =item i_tt_get_glyph(handle, inst, j)
1175 Function to see if a glyph exists and if so cache it (internal)
1177 handle - pointer to font handle
1178 inst - font instance
1179 j - charcode of glyph
1186 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1187 unsigned short load_flags, code;
1190 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1191 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1193 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1195 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1196 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1197 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1201 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1202 /* clean up the entry */
1203 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1204 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1205 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1208 /* Ok - it wasn't cached - try to get it in */
1209 load_flags = TTLOAD_SCALE_GLYPH;
1210 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1212 if ( !TT_VALID(handle->char_map) ) {
1213 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1214 if ( code >= handle->properties.num_Glyphs ) code = 0;
1215 } else code = TT_Char_Index( handle->char_map, j );
1217 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1218 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1219 i_push_error(error, "TT_New_Glyph()");
1222 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1223 mm_log((1, "Cannot allocate and load glyph: 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 i_push_error(error, "TT_Load_Glyph()");
1231 /* At this point the glyph should be allocated and loaded */
1232 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1234 /* Next get the glyph metrics */
1235 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1236 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1238 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1239 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1240 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1241 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1242 i_push_error(error, "TT_Get_Glyph_Metrics()");
1250 =item i_tt_has_chars(handle, text, len, utf8, out)
1252 Check if the given characters are defined by the font. Note that len
1253 is the number of bytes, not the number of characters (when utf8 is
1256 Returns the number of characters that were checked.
1262 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1265 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1266 handle, text, len, utf8));
1272 c = i_utf8_advance(&text, &len);
1274 i_push_error(0, "invalid UTF8 character");
1279 c = (unsigned char)*text++;
1283 if (TT_VALID(handle->char_map)) {
1284 index = TT_Char_Index(handle->char_map, c);
1287 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1288 if (index >= handle->properties.num_Glyphs)
1291 *out++ = index != 0;
1299 =item i_tt_destroy(handle)
1301 Clears the data taken by a font including all cached data such as
1304 handle - pointer to font handle
1310 i_tt_destroy( TT_Fonthandle *handle) {
1311 TT_Close_Face( handle->face );
1314 /* FIXME: Should these be freed automatically by the library?
1316 TT_Done_Instance( instance );
1318 i_tt_done_glyphs( void ) {
1321 if ( !glyphs ) return;
1323 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1333 * FreeType Rendering functions
1338 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1340 Renders a single glyph into the bit rastermap (internal)
1342 handle - pointer to font handle
1343 gmetrics - the metrics for the glyph to be rendered
1344 bit - large bitmap that is the destination for the text
1345 smallbit - small bitmap that is used only if smooth is true
1346 x_off - x offset of glyph
1347 y_off - y offset of glyph
1348 smooth - boolean (True: antialias on, False: antialias is off)
1355 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 ) {
1357 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",
1358 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1360 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1362 TT_F26Dot6 xmin, ymin, xmax, ymax;
1364 xmin = gmetrics->bbox.xMin & -64;
1365 ymin = gmetrics->bbox.yMin & -64;
1366 xmax = (gmetrics->bbox.xMax + 63) & -64;
1367 ymax = (gmetrics->bbox.yMax + 63) & -64;
1369 i_tt_clear_raster_map( small_bit );
1370 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1371 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1377 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1379 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1381 handle - pointer to font handle
1382 inst - font instance
1383 bit - large bitmap that is the destination for the text
1384 smallbit - small bitmap that is used only if smooth is true
1385 txt - string to render
1386 len - length of the string to render
1387 smooth - boolean (True: antialias on, False: antialias is off)
1394 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1395 TT_Raster_Map *small_bit, int cords[6],
1396 char const* txt, int len, int smooth, int utf8 ) {
1400 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",
1401 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1404 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1407 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 */
1412 j = i_utf8_advance(&txt, &len);
1414 i_push_error(0, "invalid UTF8 character");
1419 j = (unsigned char)*txt++;
1422 if ( !i_tt_get_glyph(handle,inst,j) )
1424 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1425 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1426 small_bit, x, y, smooth );
1427 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1435 * Functions to render rasters (single channel images) onto images
1439 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1441 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1443 im - image to dump raster on
1444 bit - bitmap that contains the text to be dumped to im
1445 xb, yb - coordinates, left edge and baseline
1446 cl - color to use for text
1447 smooth - boolean (True: antialias on, False: antialias is off)
1454 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1458 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));
1460 bmap = (char *)bit->bitmap;
1464 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1465 c=(255*bmap[y*(bit->cols)+x])/4;
1467 i_gpix(im,x+xb,y+yb,&val);
1468 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1469 i_ppix(im,x+xb,y+yb,&val);
1474 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1475 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1477 i_gpix(im,x+xb,y+yb,&val);
1478 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1479 i_ppix(im,x+xb,y+yb,&val);
1487 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1489 Function to dump a raster onto a single channel image in color (internal)
1491 im - image to dump raster on
1492 bit - bitmap that contains the text to be dumped to im
1493 xb, yb - coordinates, left edge and baseline
1494 channel - channel to copy to
1495 smooth - boolean (True: antialias on, False: antialias is off)
1502 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1507 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));
1509 bmap = (char *)bit->bitmap;
1512 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1513 c=(255*bmap[y*(bit->cols)+x])/4;
1514 i_gpix(im,x+xb,y+yb,&val);
1515 val.channel[channel]=c;
1516 i_ppix(im,x+xb,y+yb,&val);
1519 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1520 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1521 i_gpix(im,x+xb,y+yb,&val);
1522 val.channel[channel]=c;
1523 i_ppix(im,x+xb,y+yb,&val);
1530 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1532 interface for generating single channel raster of text (internal)
1534 handle - pointer to font handle
1535 bit - the bitmap that is allocated, rendered into and NOT freed
1536 cords - the bounding box (modified in place)
1537 points - font size to use
1538 txt - string to render
1539 len - length of the string to render
1540 smooth - boolean (True: antialias on, False: antialias is off)
1547 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1550 TT_Raster_Map small_bit;
1552 /* find or install an instance */
1553 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1554 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1558 /* calculate bounding box */
1559 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1563 width = cords[2]-cords[0];
1564 height = cords[5]-cords[4];
1566 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1568 i_tt_init_raster_map ( bit, width, height, smooth );
1569 i_tt_clear_raster_map( bit );
1570 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1572 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1575 i_tt_done_raster_map( &small_bit );
1579 if ( smooth ) i_tt_done_raster_map( &small_bit );
1586 * Exported text rendering interfaces
1591 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1593 Interface to text rendering into a single channel in an image
1595 handle - pointer to font handle
1596 im - image to render text on to
1597 xb, yb - coordinates, left edge and baseline
1598 channel - channel to render into
1599 points - font size to use
1600 txt - string to render
1601 len - length of the string to render
1602 smooth - boolean (True: antialias on, False: antialias is off)
1608 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 ) {
1610 int cords[BOUNDING_BOX_COUNT];
1611 int ascent, st_offset, y;
1615 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1617 ascent=cords[BBOX_ASCENT];
1618 st_offset=cords[BBOX_NEG_WIDTH];
1619 y = align ? yb-ascent : yb;
1621 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1622 i_tt_done_raster_map( &bit );
1629 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1631 Interface to text rendering in a single color onto an image
1633 handle - pointer to font handle
1634 im - image to render text on to
1635 xb, yb - coordinates, left edge and baseline
1636 cl - color to use for text
1637 points - font size to use
1638 txt - string to render
1639 len - length of the string to render
1640 smooth - boolean (True: antialias on, False: antialias is off)
1646 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) {
1647 int cords[BOUNDING_BOX_COUNT];
1648 int ascent, st_offset, y;
1653 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1655 ascent=cords[BBOX_ASCENT];
1656 st_offset=cords[BBOX_NEG_WIDTH];
1657 y = align ? yb-ascent : yb;
1659 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1660 i_tt_done_raster_map( &bit );
1667 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1669 Function to get texts bounding boxes given the instance of the font (internal)
1671 handle - pointer to font handle
1672 inst - font instance
1673 txt - string to measure
1674 len - length of the string to render
1675 cords - the bounding box (modified in place)
1682 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1683 int upm, casc, cdesc, first;
1694 unsigned char *ustr;
1695 ustr=(unsigned char*)txt;
1697 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));
1699 upm = handle->properties.header->Units_Per_EM;
1700 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1701 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1706 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1711 j = i_utf8_advance(&txt, &len);
1713 i_push_error(0, "invalid UTF8 character");
1718 j = (unsigned char)*txt++;
1721 if ( i_tt_get_glyph(handle,inst,j) ) {
1722 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1723 width += gm->advance / 64;
1724 casc = (gm->bbox.yMax+63) / 64;
1725 cdesc = (gm->bbox.yMin-63) / 64;
1727 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1728 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1731 start = gm->bbox.xMin / 64;
1732 ascent = (gm->bbox.yMax+63) / 64;
1733 descent = (gm->bbox.yMin-63) / 64;
1736 if (!len) { /* if at end of string */
1737 /* the right-side bearing - in case the right-side of a
1738 character goes past the right of the advance width,
1739 as is common for italic fonts
1741 rightb = gm->advance - gm->bearingX
1742 - (gm->bbox.xMax - gm->bbox.xMin);
1743 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1744 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1747 ascent = (ascent > casc ? ascent : casc );
1748 descent = (descent < cdesc ? descent : cdesc);
1752 cords[BBOX_NEG_WIDTH]=start;
1753 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1754 cords[BBOX_POS_WIDTH]=width;
1756 cords[BBOX_POS_WIDTH] -= rightb / 64;
1757 cords[BBOX_GLOBAL_ASCENT]=gascent;
1758 cords[BBOX_DESCENT]=descent;
1759 cords[BBOX_ASCENT]=ascent;
1760 cords[BBOX_ADVANCE_WIDTH] = width;
1761 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1763 return BBOX_RIGHT_BEARING + 1;
1768 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1770 Interface to get a strings bounding box
1772 handle - pointer to font handle
1773 points - font size to use
1774 txt - string to render
1775 len - length of the string to render
1776 cords - the bounding box (modified in place)
1782 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1786 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1788 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1789 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1790 mm_log((1,"i_tt_text: get instance failed\n"));
1794 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1798 =item i_tt_face_name(handle, name_buf, name_buf_size)
1800 Retrieve's the font's postscript name.
1802 This is complicated by the need to handle encodings and so on.
1807 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1808 TT_Face_Properties props;
1811 TT_UShort platform_id, encoding_id, lang_id, name_id;
1814 int want_index = -1; /* an acceptable but not perfect name */
1819 TT_Get_Face_Properties(handle->face, &props);
1820 name_count = props.num_Names;
1821 for (i = 0; i < name_count; ++i) {
1822 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1825 TT_Get_Name_String(handle->face, i, &name, &name_len);
1827 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1828 && name_id == TT_NAME_ID_PS_NAME) {
1829 int might_want_index = -1;
1830 int might_score = 0;
1831 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1833 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1834 /* exactly what we want */
1839 if (platform_id == TT_PLATFORM_MICROSOFT
1840 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1841 /* any english is good */
1842 might_want_index = i;
1845 /* there might be something in between */
1847 /* anything non-unicode is better than nothing */
1848 might_want_index = i;
1851 if (might_score > score) {
1852 score = might_score;
1853 want_index = might_want_index;
1858 if (want_index != -1) {
1859 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1861 strncpy(name_buf, name, name_buf_size);
1862 name_buf[name_buf_size-1] = '\0';
1864 return strlen(name) + 1;
1867 i_push_error(0, "no face name present");
1872 void i_tt_dump_names(TT_Fonthandle *handle) {
1873 TT_Face_Properties props;
1876 TT_UShort platform_id, encoding_id, lang_id, name_id;
1880 TT_Get_Face_Properties(handle->face, &props);
1881 name_count = props.num_Names;
1882 for (i = 0; i < name_count; ++i) {
1883 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1885 TT_Get_Name_String(handle->face, i, &name, &name_len);
1887 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1888 encoding_id, lang_id, name_id);
1889 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1890 printf("(unicode)\n");
1893 printf("'%s'\n", name);
1899 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1900 size_t name_buf_size) {
1908 if (!handle->loaded_names) {
1910 mm_log((1, "Loading PS Names"));
1911 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1912 ++handle->loaded_names;
1915 if (handle->load_cond) {
1916 i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1920 index = TT_Char_Index(handle->char_map, ch);
1922 i_push_error(0, "no such character");
1926 rc = TT_Get_PS_Name(handle->face, index, &psname);
1929 i_push_error(rc, "error getting name");
1933 strncpy(name_buf, psname, name_buf_size);
1934 name_buf[name_buf_size-1] = '\0';
1936 return strlen(psname) + 1;
1938 mm_log((1, "FTXPOST extension not enabled\n"));
1940 i_push_error(0, "Use of FTXPOST extension disabled");
1947 =item i_tt_push_error(code)
1949 Push an error message and code onto the Imager error stack.
1954 i_tt_push_error(TT_Error rc) {
1956 TT_String const *msg = TT_ErrToString18(rc);
1958 i_push_error(rc, msg);
1960 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1964 #endif /* HAVE_LIBTT */
1972 Arnar M. Hrafnkelsson <addi@umich.edu>