18 font.c - implements font handling functions for t1 and truetype fonts
25 fontnum = i_t1_new(path_to_pfb, path_to_afm);
26 i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
27 rc = i_t1_destroy(fontnum);
31 handle = i_tt_new(path_to_ttf);
32 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
39 font.c implements font creation, rendering, bounding box functions and
42 =head1 FUNCTION REFERENCE
44 Some of these functions are internal.
55 Initialize font rendering libraries if they are avaliable.
61 i_init_fonts(int t1log) {
62 mm_log((1,"Initializing fonts\n"));
77 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
85 static int t1_get_flags(char const *flags);
86 static char *t1_from_utf8(char const *in, int len, int *outlen);
88 static void t1_push_error(void);
90 static int t1_active_fonts = 0;
91 static int t1_initialized = 0;
94 =item i_init_t1(t1log)
96 Initializes the t1lib font rendering engine.
102 i_init_t1(int t1log) {
103 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
104 mm_log((1,"init_t1()\n"));
106 if (t1_active_fonts) {
107 mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
111 if (t1_initialized) {
116 init_flags |= LOGFILE;
117 if ((T1_InitLib(init_flags) == NULL)){
118 mm_log((1,"Initialization of t1lib failed\n"));
121 T1_SetLogLevel(T1LOG_DEBUG);
122 i_t1_set_aa(1); /* Default Antialias value */
133 Shuts the t1lib font rendering engine down.
135 This it seems that this function is never used.
148 =item i_t1_new(pfb, afm)
150 Loads the fonts with the given filenames, returns its font id
152 pfb - path to pfb file for font
153 afm - path to afm file for font
159 i_t1_new(char *pfb,char *afm) {
162 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
163 font_id = T1_AddFont(pfb);
165 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
170 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
171 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
180 =item i_t1_destroy(font_id)
182 Frees resources for a t1 font with given font id.
184 font_id - number of the font to free
190 i_t1_destroy(int font_id) {
191 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
195 return T1_DeleteFont(font_id);
200 =item i_t1_set_aa(st)
202 Sets the antialiasing level of the t1 library.
204 st - 0 = NONE, 1 = LOW, 2 = HIGH.
210 i_t1_set_aa(int st) {
212 unsigned long cst[17];
215 T1_AASetBitsPerPixel( 8 );
216 T1_AASetLevel( T1_AA_NONE );
217 T1_AANSetGrayValues( 0, 255 );
218 mm_log((1,"setting T1 antialias to none\n"));
221 T1_AASetBitsPerPixel( 8 );
222 T1_AASetLevel( T1_AA_LOW );
223 T1_AASetGrayValues( 0,65,127,191,255 );
224 mm_log((1,"setting T1 antialias to low\n"));
227 T1_AASetBitsPerPixel(8);
228 T1_AASetLevel(T1_AA_HIGH);
229 for(i=0;i<17;i++) cst[i]=(i*255)/16;
230 T1_AAHSetGrayValues( cst );
231 mm_log((1,"setting T1 antialias to high\n"));
237 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
239 Interface to text rendering into a single channel in an image
241 im pointer to image structure
242 xb x coordinate of start of string
243 yb y coordinate of start of string ( see align )
244 channel - destination channel
245 fontnum - t1 library font id
246 points - number of points in fontheight
247 str - string to render
249 align - (0 - top of font glyph | 1 - baseline )
255 i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) {
259 int mod_flags = t1_get_flags(flags);
261 unsigned int ch_mask_store;
263 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
267 char *work = t1_from_utf8(str, len, &worklen);
268 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
272 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
277 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
278 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
279 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
280 mm_log((1,"bpp: %d\n",glyph->bpp));
282 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
283 ysize=glyph->metrics.ascent-glyph->metrics.descent;
285 mm_log((1,"width: %d height: %d\n",xsize,ysize));
287 ch_mask_store=im->ch_mask;
288 im->ch_mask=1<<channel;
290 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
292 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
293 val.channel[channel]=glyph->bits[y*xsize+x];
294 i_ppix(im,x+xb,y+yb,&val);
297 im->ch_mask=ch_mask_store;
303 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
305 function to get a strings bounding box given the font id and sizes
307 handle - pointer to font handle
308 fontnum - t1 library font id
309 points - number of points in fontheight
310 str - string to measure
312 cords - the bounding box (modified in place)
318 i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
321 int mod_flags = t1_get_flags(flags);
324 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
325 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
328 char *work = t1_from_utf8(str, len, &worklen);
329 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
333 bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
335 gbbox = T1_GetFontBBox(fontnum);
336 advance = T1_GetStringWidth(fontnum, str, len, 0, mod_flags);
338 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
339 (int)(bbox.llx*points/1000),
340 (int)(gbbox.lly*points/1000),
341 (int)(bbox.urx*points/1000),
342 (int)(gbbox.ury*points/1000),
343 (int)(bbox.lly*points/1000),
344 (int)(bbox.ury*points/1000) ));
347 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
348 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
350 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
351 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
353 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
354 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
356 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
357 cords[BBOX_RIGHT_BEARING] =
358 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
360 return BBOX_RIGHT_BEARING+1;
365 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
367 Interface to text rendering in a single color onto an image
369 im - pointer to image structure
370 xb - x coordinate of start of string
371 yb - y coordinate of start of string ( see align )
372 cl - color to draw the text in
373 fontnum - t1 library font id
374 points - number of points in fontheight
375 str - char pointer to string to render
377 align - (0 - top of font glyph | 1 - baseline )
383 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) {
385 int xsize,ysize,x,y,ch;
388 int mod_flags = t1_get_flags(flags);
390 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
394 char *work = t1_from_utf8(str, len, &worklen);
395 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
399 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
404 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
405 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
406 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
407 mm_log((1,"bpp: %d\n",glyph->bpp));
409 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
410 ysize=glyph->metrics.ascent-glyph->metrics.descent;
412 mm_log((1,"width: %d height: %d\n",xsize,ysize));
414 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
416 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
417 c=glyph->bits[y*xsize+x];
419 i_gpix(im,x+xb,y+yb,&val);
420 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
421 i_ppix(im,x+xb,y+yb,&val);
427 =item t1_get_flags(flags)
429 Processes the characters in I<flags> to create a mod_flags value used
430 by some T1Lib functions.
435 t1_get_flags(char const *flags) {
436 int mod_flags = T1_KERNING;
440 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
441 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
442 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
443 /* ignore anything we don't recognize */
451 =item t1_from_utf8(char const *in, int len, int *outlen)
453 Produces an unencoded version of I<in> by dropping any Unicode
456 Returns a newly allocated buffer which should be freed with myfree().
457 Sets *outlen to the number of bytes used in the output string.
463 t1_from_utf8(char const *in, int len, int *outlen) {
464 /* at this point len is from a perl SV, so can't approach MAXINT */
465 char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
470 c = i_utf8_advance(&in, &len);
473 i_push_error(0, "invalid UTF8 character");
476 /* yeah, just drop them */
488 =item i_t1_has_chars(font_num, text, len, utf8, out)
490 Check if the given characters are defined by the font. Note that len
491 is the number of bytes, not the number of characters (when utf8 is
494 out[char index] will be true if the character exists.
496 Accepts UTF-8, but since T1 can only have 256 characters, any chars
497 with values over 255 will simply be returned as false.
499 Returns the number of characters that were checked.
505 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
509 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
510 font_num, text, len, utf8));
513 if (T1_LoadFont(font_num)) {
521 c = i_utf8_advance(&text, &len);
523 i_push_error(0, "invalid UTF8 character");
528 c = (unsigned char)*text++;
533 /* limit of 256 characters for T1 */
537 char const * name = T1_GetCharName(font_num, (unsigned char)c);
540 *out++ = strcmp(name, ".notdef") != 0;
543 mm_log((2, " No name found for character %lx\n", c));
554 =item i_t1_face_name(font_num, name_buf, name_buf_size)
556 Copies the face name of the given C<font_num> to C<name_buf>. Returns
557 the number of characters required to store the name (which can be
558 larger than C<name_buf_size>, including the space required to store
559 the terminating NUL).
561 If name_buf is too small (as specified by name_buf_size) then the name
562 will be truncated. name_buf will always be NUL termintaed.
568 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
572 if (T1_LoadFont(font_num)) {
576 name = T1_GetFontName(font_num);
579 strncpy(name_buf, name, name_buf_size);
580 name_buf[name_buf_size-1] = '\0';
581 return strlen(name) + 1;
590 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
591 size_t name_buf_size) {
598 if (T1_LoadFont(font_num)) {
602 name = T1_GetCharName(font_num, (unsigned char)ch);
604 if (strcmp(name, ".notdef")) {
605 strncpy(name_buf, name, name_buf_size);
606 name_buf[name_buf_size-1] = '\0';
607 return strlen(name) + 1;
620 t1_push_error(void) {
623 i_push_error(0, "No error");
626 #ifdef T1ERR_SCAN_FONT_FORMAT
627 case T1ERR_SCAN_FONT_FORMAT:
628 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
632 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
633 case T1ERR_SCAN_FILE_OPEN_ERR:
634 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
638 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
639 case T1ERR_SCAN_OUT_OF_MEMORY:
640 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
644 #ifdef T1ERR_SCAN_ERROR
645 case T1ERR_SCAN_ERROR:
646 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
650 #ifdef T1ERR_SCAN_FILE_EOF
651 case T1ERR_SCAN_FILE_EOF:
652 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
656 #ifdef T1ERR_PATH_ERROR
657 case T1ERR_PATH_ERROR:
658 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
662 #ifdef T1ERR_PARSE_ERROR
663 case T1ERR_PARSE_ERROR:
664 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
668 #ifdef T1ERR_TYPE1_ABORT
669 case T1ERR_TYPE1_ABORT:
670 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
674 #ifdef T1ERR_INVALID_FONTID
675 case T1ERR_INVALID_FONTID:
676 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
680 #ifdef T1ERR_INVALID_PARAMETER
681 case T1ERR_INVALID_PARAMETER:
682 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
686 #ifdef T1ERR_OP_NOT_PERMITTED
687 case T1ERR_OP_NOT_PERMITTED:
688 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
692 #ifdef T1ERR_ALLOC_MEM
693 case T1ERR_ALLOC_MEM:
694 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
698 #ifdef T1ERR_FILE_OPEN_ERR
699 case T1ERR_FILE_OPEN_ERR:
700 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
704 #ifdef T1ERR_UNSPECIFIED
705 case T1ERR_UNSPECIFIED:
706 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
710 #ifdef T1ERR_NO_AFM_DATA
711 case T1ERR_NO_AFM_DATA:
712 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
718 i_push_error(T1ERR_X11, "X11");
722 #ifdef T1ERR_COMPOSITE_CHAR
723 case T1ERR_COMPOSITE_CHAR:
724 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
729 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
733 #endif /* HAVE_LIBT1 */
736 /* Truetype font support */
739 /* These are enabled by default when configuring Freetype 1.x
740 I haven't a clue how to reliably detect it at compile time.
742 We need a compilation probe in Makefile.PL
747 #include <freetype.h>
755 #include <ftxerr18.h>
758 /* some versions of FT1.x don't seem to define this - it's font defined
759 so it won't change */
760 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
761 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
764 /* convert a code point into an index in the glyph cache */
765 #define TT_HASH(x) ((x) & 0xFF)
767 typedef struct i_glyph_entry_ {
772 #define TT_NOCHAR (~0UL)
774 struct TT_Instancehandle_ {
775 TT_Instance instance;
776 TT_Instance_Metrics imetrics;
777 TT_Glyph_Metrics gmetrics[256];
778 i_tt_glyph_entry glyphs[256];
784 typedef struct TT_Instancehandle_ TT_Instancehandle;
786 struct TT_Fonthandle_ {
788 TT_Face_Properties properties;
789 TT_Instancehandle instanceh[TT_CHC];
799 #define USTRCT(x) ((x).z)
800 #define TT_VALID( handle ) ( ( handle ).z != NULL )
802 static void i_tt_push_error(TT_Error rc);
806 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
807 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
808 static void i_tt_done_raster_map( TT_Raster_Map *bit );
809 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
810 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
811 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
813 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
814 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
815 int x_off, int y_off, int smooth );
817 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
818 TT_Raster_Map *small_bit, int cords[6],
819 char const* txt, int len, int smooth, int utf8 );
820 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
821 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
823 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
824 float points, char const* txt, int len, int smooth, int utf8 );
825 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
828 /* static globals needed */
830 static TT_Engine engine;
831 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
832 static int LTT_hinted = 1; /* FIXME: this too */
843 Initializes the freetype font rendering engine
851 mm_log((1,"init_tt()\n"));
852 error = TT_Init_FreeType( &engine );
854 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
859 error = TT_Init_Post_Extension( engine );
861 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
871 =item i_tt_get_instance(handle, points, smooth)
873 Finds a points+smooth instance or if one doesn't exist in the cache
874 allocates room and returns its cache entry
876 fontname - path to the font to load
877 handle - handle to the font.
878 points - points of the requested font
879 smooth - boolean (True: antialias on, False: antialias is off)
886 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
890 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
891 handle,points,smooth));
893 if (smooth == -1) { /* Smooth doesn't matter for this search */
894 for(i=0;i<TT_CHC;i++) {
895 if (handle->instanceh[i].ptsize==points) {
896 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
900 smooth=1; /* We will be adding a font - add it as smooth then */
901 } else { /* Smooth doesn't matter for this search */
902 for(i=0;i<TT_CHC;i++) {
903 if (handle->instanceh[i].ptsize == points
904 && handle->instanceh[i].smooth == smooth) {
905 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
911 /* Found the instance in the cache - return the cache index */
913 for(idx=0;idx<TT_CHC;idx++) {
914 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
917 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
918 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
919 USTRCT(handle->instanceh[idx].instance) ));
921 if ( USTRCT(handle->instanceh[idx].instance) ) {
922 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
924 /* Free cached glyphs */
926 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
927 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
930 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
931 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
934 /* Free instance if needed */
935 TT_Done_Instance( handle->instanceh[idx].instance );
938 /* create and initialize instance */
939 /* FIXME: probably a memory leak on fail */
941 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
942 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
943 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
946 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
950 /* Now that the instance should the inplace we need to lower all of the
951 ru counts and put `this' one with the highest entry */
953 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
955 handle->instanceh[idx].order=TT_CHC-1;
956 handle->instanceh[idx].ptsize=points;
957 handle->instanceh[idx].smooth=smooth;
958 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
960 /* Zero the memory for the glyph storage so they are not thought as
961 cached if they haven't been cached since this new font was loaded */
964 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
965 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
973 =item i_tt_new(fontname)
975 Creates a new font handle object, finds a character map and initialise the
976 the font handle's cache
978 fontname - path to the font to load
984 i_tt_new(char *fontname) {
986 TT_Fonthandle *handle;
988 unsigned short platform,encoding;
992 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
994 /* allocate memory for the structure */
996 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
998 /* load the typeface */
999 error = TT_Open_Face( engine, fontname, &handle->face );
1001 if ( error == TT_Err_Could_Not_Open_File ) {
1002 mm_log((1, "Could not find/open %s.\n", fontname ));
1005 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1008 i_tt_push_error(error);
1012 TT_Get_Face_Properties( handle->face, &(handle->properties) );
1014 /* First, look for a Unicode charmap */
1015 n = handle->properties.num_CharMaps;
1016 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1018 for ( i = 0; i < n; i++ ) {
1019 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
1020 if ( (platform == 3 && encoding == 1 )
1021 || (platform == 0 && encoding == 0 ) ) {
1022 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1023 platform, encoding));
1024 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1028 if (!USTRCT(handle->char_map) && n != 0) {
1029 /* just use the first one */
1030 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1033 /* Zero the pointsizes - and ordering */
1035 for(i=0;i<TT_CHC;i++) {
1036 USTRCT(handle->instanceh[i].instance)=NULL;
1037 handle->instanceh[i].order=i;
1038 handle->instanceh[i].ptsize=0;
1039 handle->instanceh[i].smooth=-1;
1043 handle->loaded_names = 0;
1046 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1053 * raster map management
1057 =item i_tt_init_raster_map(bit, width, height, smooth)
1059 Allocates internal memory for the bitmap as needed by the parameters (internal)
1061 bit - bitmap to allocate into
1062 width - width of the bitmap
1063 height - height of the bitmap
1064 smooth - boolean (True: antialias on, False: antialias is off)
1071 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1073 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1076 bit->width = ( width + 3 ) & -4;
1077 bit->flow = TT_Flow_Down;
1080 bit->cols = bit->width;
1081 bit->size = bit->rows * bit->width;
1083 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1084 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1087 if (bit->size / bit->rows != bit->cols) {
1088 m_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1089 bit->width, bit->rows);
1092 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 ));
1094 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1095 if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1100 =item i_tt_clear_raster_map(bit)
1102 Frees the bitmap data and sets pointer to NULL (internal)
1104 bit - bitmap to free
1111 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1112 myfree( bit->bitmap );
1118 =item i_tt_clear_raster_map(bit)
1120 Clears the specified bitmap (internal)
1122 bit - bitmap to zero
1130 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1131 memset( bit->bitmap, 0, bit->size );
1136 =item i_tt_blit_or(dst, src, x_off, y_off)
1138 function that blits one raster map into another (internal)
1140 dst - destination bitmap
1142 x_off - x offset into the destination bitmap
1143 y_off - y offset into the destination bitmap
1150 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1155 x1 = x_off < 0 ? -x_off : 0;
1156 y1 = y_off < 0 ? -y_off : 0;
1158 x2 = (int)dst->cols - x_off;
1159 if ( x2 > src->cols ) x2 = src->cols;
1161 y2 = (int)dst->rows - y_off;
1162 if ( y2 > src->rows ) y2 = src->rows;
1164 if ( x1 >= x2 ) return;
1166 /* do the real work now */
1168 for ( y = y1; y < y2; ++y ) {
1169 s = ( (char*)src->bitmap ) + y * src->cols + x1;
1170 d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1172 for ( x = x1; x < x2; ++x ) {
1181 /* useful for debugging */
1184 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1186 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1187 for (y = 0; y < bit->rows; ++y) {
1188 fprintf(out, "%2d:", y);
1189 for (x = 0; x < bit->cols; ++x) {
1190 if ((x & 7) == 0 && x) putc(' ', out);
1191 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1200 =item i_tt_get_glyph(handle, inst, j)
1202 Function to see if a glyph exists and if so cache it (internal)
1204 handle - pointer to font handle
1205 inst - font instance
1206 j - charcode of glyph
1213 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1214 unsigned short load_flags, code;
1217 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1218 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1220 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1222 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1223 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1224 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1228 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1229 /* clean up the entry */
1230 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1231 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1232 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1235 /* Ok - it wasn't cached - try to get it in */
1236 load_flags = TTLOAD_SCALE_GLYPH;
1237 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1239 if ( !TT_VALID(handle->char_map) ) {
1240 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1241 if ( code >= handle->properties.num_Glyphs ) code = 0;
1242 } else code = TT_Char_Index( handle->char_map, j );
1244 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1245 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1246 i_push_error(error, "TT_New_Glyph()");
1249 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1250 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1252 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1253 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1254 i_push_error(error, "TT_Load_Glyph()");
1258 /* At this point the glyph should be allocated and loaded */
1259 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1261 /* Next get the glyph metrics */
1262 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1263 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1265 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1266 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1267 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1268 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1269 i_push_error(error, "TT_Get_Glyph_Metrics()");
1277 =item i_tt_has_chars(handle, text, len, utf8, out)
1279 Check if the given characters are defined by the font. Note that len
1280 is the number of bytes, not the number of characters (when utf8 is
1283 Returns the number of characters that were checked.
1289 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1292 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1293 handle, text, len, utf8));
1299 c = i_utf8_advance(&text, &len);
1301 i_push_error(0, "invalid UTF8 character");
1306 c = (unsigned char)*text++;
1310 if (TT_VALID(handle->char_map)) {
1311 index = TT_Char_Index(handle->char_map, c);
1314 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1315 if (index >= handle->properties.num_Glyphs)
1318 *out++ = index != 0;
1326 =item i_tt_destroy(handle)
1328 Clears the data taken by a font including all cached data such as
1331 handle - pointer to font handle
1337 i_tt_destroy( TT_Fonthandle *handle) {
1338 TT_Close_Face( handle->face );
1341 /* FIXME: Should these be freed automatically by the library?
1343 TT_Done_Instance( instance );
1345 i_tt_done_glyphs( void ) {
1348 if ( !glyphs ) return;
1350 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1360 * FreeType Rendering functions
1365 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1367 Renders a single glyph into the bit rastermap (internal)
1369 handle - pointer to font handle
1370 gmetrics - the metrics for the glyph to be rendered
1371 bit - large bitmap that is the destination for the text
1372 smallbit - small bitmap that is used only if smooth is true
1373 x_off - x offset of glyph
1374 y_off - y offset of glyph
1375 smooth - boolean (True: antialias on, False: antialias is off)
1382 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 ) {
1384 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",
1385 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1387 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1389 TT_F26Dot6 xmin, ymin, xmax, ymax;
1391 xmin = gmetrics->bbox.xMin & -64;
1392 ymin = gmetrics->bbox.yMin & -64;
1393 xmax = (gmetrics->bbox.xMax + 63) & -64;
1394 ymax = (gmetrics->bbox.yMax + 63) & -64;
1396 i_tt_clear_raster_map( small_bit );
1397 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1398 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1404 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1406 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1408 handle - pointer to font handle
1409 inst - font instance
1410 bit - large bitmap that is the destination for the text
1411 smallbit - small bitmap that is used only if smooth is true
1412 txt - string to render
1413 len - length of the string to render
1414 smooth - boolean (True: antialias on, False: antialias is off)
1421 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1422 TT_Raster_Map *small_bit, int cords[6],
1423 char const* txt, int len, int smooth, int utf8 ) {
1427 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",
1428 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1431 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1434 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 */
1439 j = i_utf8_advance(&txt, &len);
1441 i_push_error(0, "invalid UTF8 character");
1446 j = (unsigned char)*txt++;
1449 if ( !i_tt_get_glyph(handle,inst,j) )
1451 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1452 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1453 small_bit, x, y, smooth );
1454 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1462 * Functions to render rasters (single channel images) onto images
1466 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1468 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1470 im - image to dump raster on
1471 bit - bitmap that contains the text to be dumped to im
1472 xb, yb - coordinates, left edge and baseline
1473 cl - color to use for text
1474 smooth - boolean (True: antialias on, False: antialias is off)
1481 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
1485 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));
1487 bmap = (char *)bit->bitmap;
1491 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1492 c=(255*bmap[y*(bit->cols)+x])/4;
1494 i_gpix(im,x+xb,y+yb,&val);
1495 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1496 i_ppix(im,x+xb,y+yb,&val);
1501 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1502 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1504 i_gpix(im,x+xb,y+yb,&val);
1505 for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
1506 i_ppix(im,x+xb,y+yb,&val);
1514 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1516 Function to dump a raster onto a single channel image in color (internal)
1518 im - image to dump raster on
1519 bit - bitmap that contains the text to be dumped to im
1520 xb, yb - coordinates, left edge and baseline
1521 channel - channel to copy to
1522 smooth - boolean (True: antialias on, False: antialias is off)
1529 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1534 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));
1536 bmap = (char *)bit->bitmap;
1539 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1540 c=(255*bmap[y*(bit->cols)+x])/4;
1541 i_gpix(im,x+xb,y+yb,&val);
1542 val.channel[channel]=c;
1543 i_ppix(im,x+xb,y+yb,&val);
1546 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1547 c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
1548 i_gpix(im,x+xb,y+yb,&val);
1549 val.channel[channel]=c;
1550 i_ppix(im,x+xb,y+yb,&val);
1557 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1559 interface for generating single channel raster of text (internal)
1561 handle - pointer to font handle
1562 bit - the bitmap that is allocated, rendered into and NOT freed
1563 cords - the bounding box (modified in place)
1564 points - font size to use
1565 txt - string to render
1566 len - length of the string to render
1567 smooth - boolean (True: antialias on, False: antialias is off)
1574 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1577 TT_Raster_Map small_bit;
1579 /* find or install an instance */
1580 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1581 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1585 /* calculate bounding box */
1586 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1590 width = cords[2]-cords[0];
1591 height = cords[5]-cords[4];
1593 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1595 i_tt_init_raster_map ( bit, width, height, smooth );
1596 i_tt_clear_raster_map( bit );
1597 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1599 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1602 i_tt_done_raster_map( &small_bit );
1606 if ( smooth ) i_tt_done_raster_map( &small_bit );
1613 * Exported text rendering interfaces
1618 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1620 Interface to text rendering into a single channel in an image
1622 handle - pointer to font handle
1623 im - image to render text on to
1624 xb, yb - coordinates, left edge and baseline
1625 channel - channel to render into
1626 points - font size to use
1627 txt - string to render
1628 len - length of the string to render
1629 smooth - boolean (True: antialias on, False: antialias is off)
1635 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 ) {
1637 int cords[BOUNDING_BOX_COUNT];
1638 int ascent, st_offset, y;
1642 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1644 ascent=cords[BBOX_ASCENT];
1645 st_offset=cords[BBOX_NEG_WIDTH];
1646 y = align ? yb-ascent : yb;
1648 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1649 i_tt_done_raster_map( &bit );
1656 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1658 Interface to text rendering in a single color onto an image
1660 handle - pointer to font handle
1661 im - image to render text on to
1662 xb, yb - coordinates, left edge and baseline
1663 cl - color to use for text
1664 points - font size to use
1665 txt - string to render
1666 len - length of the string to render
1667 smooth - boolean (True: antialias on, False: antialias is off)
1673 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) {
1674 int cords[BOUNDING_BOX_COUNT];
1675 int ascent, st_offset, y;
1680 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1682 ascent=cords[BBOX_ASCENT];
1683 st_offset=cords[BBOX_NEG_WIDTH];
1684 y = align ? yb-ascent : yb;
1686 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1687 i_tt_done_raster_map( &bit );
1694 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1696 Function to get texts bounding boxes given the instance of the font (internal)
1698 handle - pointer to font handle
1699 inst - font instance
1700 txt - string to measure
1701 len - length of the string to render
1702 cords - the bounding box (modified in place)
1709 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1710 int upm, casc, cdesc, first;
1721 unsigned char *ustr;
1722 ustr=(unsigned char*)txt;
1724 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));
1726 upm = handle->properties.header->Units_Per_EM;
1727 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1728 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1733 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1738 j = i_utf8_advance(&txt, &len);
1740 i_push_error(0, "invalid UTF8 character");
1745 j = (unsigned char)*txt++;
1748 if ( i_tt_get_glyph(handle,inst,j) ) {
1749 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1750 width += gm->advance / 64;
1751 casc = (gm->bbox.yMax+63) / 64;
1752 cdesc = (gm->bbox.yMin-63) / 64;
1754 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1755 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1758 start = gm->bbox.xMin / 64;
1759 ascent = (gm->bbox.yMax+63) / 64;
1760 descent = (gm->bbox.yMin-63) / 64;
1763 if (!len) { /* if at end of string */
1764 /* the right-side bearing - in case the right-side of a
1765 character goes past the right of the advance width,
1766 as is common for italic fonts
1768 rightb = gm->advance - gm->bearingX
1769 - (gm->bbox.xMax - gm->bbox.xMin);
1770 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1771 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1774 ascent = (ascent > casc ? ascent : casc );
1775 descent = (descent < cdesc ? descent : cdesc);
1779 cords[BBOX_NEG_WIDTH]=start;
1780 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1781 cords[BBOX_POS_WIDTH]=width;
1783 cords[BBOX_POS_WIDTH] -= rightb / 64;
1784 cords[BBOX_GLOBAL_ASCENT]=gascent;
1785 cords[BBOX_DESCENT]=descent;
1786 cords[BBOX_ASCENT]=ascent;
1787 cords[BBOX_ADVANCE_WIDTH] = width;
1788 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1790 return BBOX_RIGHT_BEARING + 1;
1795 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1797 Interface to get a strings bounding box
1799 handle - pointer to font handle
1800 points - font size to use
1801 txt - string to render
1802 len - length of the string to render
1803 cords - the bounding box (modified in place)
1809 i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], int utf8) {
1813 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1815 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1816 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1817 mm_log((1,"i_tt_text: get instance failed\n"));
1821 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1825 =item i_tt_face_name(handle, name_buf, name_buf_size)
1827 Retrieve's the font's postscript name.
1829 This is complicated by the need to handle encodings and so on.
1834 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1835 TT_Face_Properties props;
1838 TT_UShort platform_id, encoding_id, lang_id, name_id;
1841 int want_index = -1; /* an acceptable but not perfect name */
1846 TT_Get_Face_Properties(handle->face, &props);
1847 name_count = props.num_Names;
1848 for (i = 0; i < name_count; ++i) {
1849 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1852 TT_Get_Name_String(handle->face, i, &name, &name_len);
1854 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1855 && name_id == TT_NAME_ID_PS_NAME) {
1856 int might_want_index = -1;
1857 int might_score = 0;
1858 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1860 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1861 /* exactly what we want */
1866 if (platform_id == TT_PLATFORM_MICROSOFT
1867 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1868 /* any english is good */
1869 might_want_index = i;
1872 /* there might be something in between */
1874 /* anything non-unicode is better than nothing */
1875 might_want_index = i;
1878 if (might_score > score) {
1879 score = might_score;
1880 want_index = might_want_index;
1885 if (want_index != -1) {
1886 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1888 strncpy(name_buf, name, name_buf_size);
1889 name_buf[name_buf_size-1] = '\0';
1891 return strlen(name) + 1;
1894 i_push_error(0, "no face name present");
1899 void i_tt_dump_names(TT_Fonthandle *handle) {
1900 TT_Face_Properties props;
1903 TT_UShort platform_id, encoding_id, lang_id, name_id;
1907 TT_Get_Face_Properties(handle->face, &props);
1908 name_count = props.num_Names;
1909 for (i = 0; i < name_count; ++i) {
1910 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1912 TT_Get_Name_String(handle->face, i, &name, &name_len);
1914 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1915 encoding_id, lang_id, name_id);
1916 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1917 printf("(unicode)\n");
1920 printf("'%s'\n", name);
1926 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1927 size_t name_buf_size) {
1935 if (!handle->loaded_names) {
1937 mm_log((1, "Loading PS Names"));
1938 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1939 ++handle->loaded_names;
1942 if (handle->load_cond) {
1943 i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
1947 index = TT_Char_Index(handle->char_map, ch);
1949 i_push_error(0, "no such character");
1953 rc = TT_Get_PS_Name(handle->face, index, &psname);
1956 i_push_error(rc, "error getting name");
1960 strncpy(name_buf, psname, name_buf_size);
1961 name_buf[name_buf_size-1] = '\0';
1963 return strlen(psname) + 1;
1965 mm_log((1, "FTXPOST extension not enabled\n"));
1967 i_push_error(0, "Use of FTXPOST extension disabled");
1974 =item i_tt_push_error(code)
1976 Push an error message and code onto the Imager error stack.
1981 i_tt_push_error(TT_Error rc) {
1983 TT_String const *msg = TT_ErrToString18(rc);
1985 i_push_error(rc, msg);
1987 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
1991 #endif /* HAVE_LIBTT */
1999 Arnar M. Hrafnkelsson <addi@umich.edu>