19 font.c - implements font handling functions for t1 and truetype fonts
26 fontnum = i_t1_new(path_to_pfb, path_to_afm);
27 i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
28 rc = i_t1_destroy(fontnum);
32 handle = i_tt_new(path_to_ttf);
33 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
40 font.c implements font creation, rendering, bounding box functions and
43 =head1 FUNCTION REFERENCE
45 Some of these functions are internal.
56 Initialize font rendering libraries if they are avaliable.
62 i_init_fonts(int t1log) {
63 mm_log((1,"Initializing fonts\n"));
78 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
86 static int t1_get_flags(char const *flags);
87 static char *t1_from_utf8(char const *in, int len, int *outlen);
89 static void t1_push_error(void);
91 static int t1_active_fonts = 0;
92 static int t1_initialized = 0;
95 =item i_init_t1(t1log)
97 Initializes the t1lib font rendering engine.
103 i_init_t1(int t1log) {
104 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
105 mm_log((1,"init_t1()\n"));
107 if (t1_active_fonts) {
108 mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
112 if (t1_initialized) {
117 init_flags |= LOGFILE;
118 if ((T1_InitLib(init_flags) == NULL)){
119 mm_log((1,"Initialization of t1lib failed\n"));
122 T1_SetLogLevel(T1LOG_DEBUG);
123 i_t1_set_aa(1); /* Default Antialias value */
134 Shuts the t1lib font rendering engine down.
136 This it seems that this function is never used.
149 =item i_t1_new(pfb, afm)
151 Loads the fonts with the given filenames, returns its font id
153 pfb - path to pfb file for font
154 afm - path to afm file for font
160 i_t1_new(char *pfb,char *afm) {
163 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
164 font_id = T1_AddFont(pfb);
166 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
171 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
172 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
181 =item i_t1_destroy(font_id)
183 Frees resources for a t1 font with given font id.
185 font_id - number of the font to free
191 i_t1_destroy(int font_id) {
192 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
196 return T1_DeleteFont(font_id);
201 =item i_t1_set_aa(st)
203 Sets the antialiasing level of the t1 library.
205 st - 0 = NONE, 1 = LOW, 2 = HIGH.
211 i_t1_set_aa(int st) {
213 unsigned long cst[17];
216 T1_AASetBitsPerPixel( 8 );
217 T1_AASetLevel( T1_AA_NONE );
218 T1_AANSetGrayValues( 0, 255 );
219 mm_log((1,"setting T1 antialias to none\n"));
222 T1_AASetBitsPerPixel( 8 );
223 T1_AASetLevel( T1_AA_LOW );
224 T1_AASetGrayValues( 0,65,127,191,255 );
225 mm_log((1,"setting T1 antialias to low\n"));
228 T1_AASetBitsPerPixel(8);
229 T1_AASetLevel(T1_AA_HIGH);
230 for(i=0;i<17;i++) cst[i]=(i*255)/16;
231 T1_AAHSetGrayValues( cst );
232 mm_log((1,"setting T1 antialias to high\n"));
238 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
240 Interface to text rendering into a single channel in an image
242 im pointer to image structure
243 xb x coordinate of start of string
244 yb y coordinate of start of string ( see align )
245 channel - destination channel
246 fontnum - t1 library font id
247 points - number of points in fontheight
248 str - string to render
250 align - (0 - top of font glyph | 1 - baseline )
256 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) {
260 int mod_flags = t1_get_flags(flags);
262 unsigned int ch_mask_store;
264 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
268 char *work = t1_from_utf8(str, len, &worklen);
269 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
273 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
278 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
279 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
280 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
281 mm_log((1,"bpp: %d\n",glyph->bpp));
283 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
284 ysize=glyph->metrics.ascent-glyph->metrics.descent;
286 mm_log((1,"width: %d height: %d\n",xsize,ysize));
288 ch_mask_store=im->ch_mask;
289 im->ch_mask=1<<channel;
291 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
293 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
294 val.channel[channel]=glyph->bits[y*xsize+x];
295 i_ppix(im,x+xb,y+yb,&val);
298 im->ch_mask=ch_mask_store;
303 t1_fix_bbox(BBox *bbox, const char *str, int len, int advance,
304 int space_position) {
305 /* never called with len == 0 */
306 if (str[0] == space_position && bbox->llx > 0)
308 if (str[len-1] == space_position && bbox->urx < advance)
310 if (bbox->lly > bbox->ury)
311 bbox->lly = bbox->ury = 0;
315 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
317 function to get a strings bounding box given the font id and sizes
319 handle - pointer to font handle
320 fontnum - t1 library font id
321 points - number of points in fontheight
322 str - string to measure
324 cords - the bounding box (modified in place)
330 i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) {
333 int mod_flags = t1_get_flags(flags);
335 int space_position = T1_GetEncodingIndex(fontnum, "space");
337 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
338 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
341 /* len == 0 has special meaning to T1lib, but it means there's
342 nothing to draw, so return that */
343 bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
349 char *work = t1_from_utf8(str, len, &worklen);
350 advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
351 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
352 t1_fix_bbox(&bbox, work, worklen, advance, space_position);
356 advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
357 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
358 t1_fix_bbox(&bbox, str, len, advance, space_position);
361 gbbox = T1_GetFontBBox(fontnum);
363 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
364 (int)(bbox.llx*points/1000),
365 (int)(gbbox.lly*points/1000),
366 (int)(bbox.urx*points/1000),
367 (int)(gbbox.ury*points/1000),
368 (int)(bbox.lly*points/1000),
369 (int)(bbox.ury*points/1000) ));
372 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
373 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
375 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
376 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
378 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
379 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
381 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
382 cords[BBOX_RIGHT_BEARING] =
383 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
385 return BBOX_RIGHT_BEARING+1;
390 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
392 Interface to text rendering in a single color onto an image
394 im - pointer to image structure
395 xb - x coordinate of start of string
396 yb - y coordinate of start of string ( see align )
397 cl - color to draw the text in
398 fontnum - t1 library font id
399 points - number of points in fontheight
400 str - char pointer to string to render
402 align - (0 - top of font glyph | 1 - baseline )
408 i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,int len,int align, int utf8, char const *flags) {
411 int mod_flags = t1_get_flags(flags);
414 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
418 char *work = t1_from_utf8(str, len, &worklen);
419 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
423 /* T1_AASetString() accepts a char * not a const char */
424 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
429 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
430 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
431 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
432 mm_log((1,"bpp: %d\n",glyph->bpp));
434 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
435 ysize=glyph->metrics.ascent-glyph->metrics.descent;
437 mm_log((1,"width: %d height: %d\n",xsize,ysize));
439 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
441 i_render_init(&r, im, xsize);
442 for(y=0;y<ysize;y++) {
443 i_render_color(&r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
451 =item t1_get_flags(flags)
453 Processes the characters in I<flags> to create a mod_flags value used
454 by some T1Lib functions.
459 t1_get_flags(char const *flags) {
460 int mod_flags = T1_KERNING;
464 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
465 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
466 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
467 /* ignore anything we don't recognize */
475 =item t1_from_utf8(char const *in, int len, int *outlen)
477 Produces an unencoded version of I<in> by dropping any Unicode
480 Returns a newly allocated buffer which should be freed with myfree().
481 Sets *outlen to the number of bytes used in the output string.
487 t1_from_utf8(char const *in, int len, int *outlen) {
488 /* at this point len is from a perl SV, so can't approach MAXINT */
489 char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
494 c = i_utf8_advance(&in, &len);
497 i_push_error(0, "invalid UTF8 character");
500 /* yeah, just drop them */
512 =item i_t1_has_chars(font_num, text, len, utf8, out)
514 Check if the given characters are defined by the font. Note that len
515 is the number of bytes, not the number of characters (when utf8 is
518 out[char index] will be true if the character exists.
520 Accepts UTF-8, but since T1 can only have 256 characters, any chars
521 with values over 255 will simply be returned as false.
523 Returns the number of characters that were checked.
529 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
533 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
534 font_num, text, len, utf8));
537 if (T1_LoadFont(font_num)) {
545 c = i_utf8_advance(&text, &len);
547 i_push_error(0, "invalid UTF8 character");
552 c = (unsigned char)*text++;
557 /* limit of 256 characters for T1 */
561 char const * name = T1_GetCharName(font_num, (unsigned char)c);
564 *out++ = strcmp(name, ".notdef") != 0;
567 mm_log((2, " No name found for character %lx\n", c));
578 =item i_t1_face_name(font_num, name_buf, name_buf_size)
580 Copies the face name of the given C<font_num> to C<name_buf>. Returns
581 the number of characters required to store the name (which can be
582 larger than C<name_buf_size>, including the space required to store
583 the terminating NUL).
585 If name_buf is too small (as specified by name_buf_size) then the name
586 will be truncated. name_buf will always be NUL termintaed.
592 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
596 if (T1_LoadFont(font_num)) {
600 name = T1_GetFontName(font_num);
603 strncpy(name_buf, name, name_buf_size);
604 name_buf[name_buf_size-1] = '\0';
605 return strlen(name) + 1;
614 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
615 size_t name_buf_size) {
622 if (T1_LoadFont(font_num)) {
626 name = T1_GetCharName(font_num, (unsigned char)ch);
628 if (strcmp(name, ".notdef")) {
629 strncpy(name_buf, name, name_buf_size);
630 name_buf[name_buf_size-1] = '\0';
631 return strlen(name) + 1;
644 t1_push_error(void) {
647 i_push_error(0, "No error");
650 #ifdef T1ERR_SCAN_FONT_FORMAT
651 case T1ERR_SCAN_FONT_FORMAT:
652 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
656 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
657 case T1ERR_SCAN_FILE_OPEN_ERR:
658 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
662 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
663 case T1ERR_SCAN_OUT_OF_MEMORY:
664 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
668 #ifdef T1ERR_SCAN_ERROR
669 case T1ERR_SCAN_ERROR:
670 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
674 #ifdef T1ERR_SCAN_FILE_EOF
675 case T1ERR_SCAN_FILE_EOF:
676 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
680 #ifdef T1ERR_PATH_ERROR
681 case T1ERR_PATH_ERROR:
682 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
686 #ifdef T1ERR_PARSE_ERROR
687 case T1ERR_PARSE_ERROR:
688 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
692 #ifdef T1ERR_TYPE1_ABORT
693 case T1ERR_TYPE1_ABORT:
694 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
698 #ifdef T1ERR_INVALID_FONTID
699 case T1ERR_INVALID_FONTID:
700 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
704 #ifdef T1ERR_INVALID_PARAMETER
705 case T1ERR_INVALID_PARAMETER:
706 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
710 #ifdef T1ERR_OP_NOT_PERMITTED
711 case T1ERR_OP_NOT_PERMITTED:
712 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
716 #ifdef T1ERR_ALLOC_MEM
717 case T1ERR_ALLOC_MEM:
718 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
722 #ifdef T1ERR_FILE_OPEN_ERR
723 case T1ERR_FILE_OPEN_ERR:
724 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
728 #ifdef T1ERR_UNSPECIFIED
729 case T1ERR_UNSPECIFIED:
730 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
734 #ifdef T1ERR_NO_AFM_DATA
735 case T1ERR_NO_AFM_DATA:
736 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
742 i_push_error(T1ERR_X11, "X11");
746 #ifdef T1ERR_COMPOSITE_CHAR
747 case T1ERR_COMPOSITE_CHAR:
748 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
753 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
757 #endif /* HAVE_LIBT1 */
760 /* Truetype font support */
763 /* These are enabled by default when configuring Freetype 1.x
764 I haven't a clue how to reliably detect it at compile time.
766 We need a compilation probe in Makefile.PL
771 #include <freetype.h>
779 #include <ftxerr18.h>
782 /* some versions of FT1.x don't seem to define this - it's font defined
783 so it won't change */
784 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
785 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
788 /* convert a code point into an index in the glyph cache */
789 #define TT_HASH(x) ((x) & 0xFF)
791 typedef struct i_glyph_entry_ {
796 #define TT_NOCHAR (~0UL)
798 struct TT_Instancehandle_ {
799 TT_Instance instance;
800 TT_Instance_Metrics imetrics;
801 TT_Glyph_Metrics gmetrics[256];
802 i_tt_glyph_entry glyphs[256];
808 typedef struct TT_Instancehandle_ TT_Instancehandle;
810 struct TT_Fonthandle_ {
812 TT_Face_Properties properties;
813 TT_Instancehandle instanceh[TT_CHC];
823 #define USTRCT(x) ((x).z)
824 #define TT_VALID( handle ) ( ( handle ).z != NULL )
826 static void i_tt_push_error(TT_Error rc);
830 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
831 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
832 static void i_tt_done_raster_map( TT_Raster_Map *bit );
833 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
834 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
835 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
837 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
838 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
839 int x_off, int y_off, int smooth );
841 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
842 TT_Raster_Map *small_bit, int cords[6],
843 char const* txt, int len, int smooth, int utf8 );
844 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
845 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
847 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
848 float points, char const* txt, int len, int smooth, int utf8 );
849 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
852 /* static globals needed */
854 static TT_Engine engine;
855 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
856 static int LTT_hinted = 1; /* FIXME: this too */
867 Initializes the freetype font rendering engine
875 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
877 mm_log((1,"init_tt()\n"));
878 error = TT_Init_FreeType( &engine );
880 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
885 error = TT_Init_Post_Extension( engine );
887 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
892 error = TT_Set_Raster_Gray_Palette(engine, palette);
894 mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
903 =item i_tt_get_instance(handle, points, smooth)
905 Finds a points+smooth instance or if one doesn't exist in the cache
906 allocates room and returns its cache entry
908 fontname - path to the font to load
909 handle - handle to the font.
910 points - points of the requested font
911 smooth - boolean (True: antialias on, False: antialias is off)
918 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
922 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
923 handle,points,smooth));
925 if (smooth == -1) { /* Smooth doesn't matter for this search */
926 for(i=0;i<TT_CHC;i++) {
927 if (handle->instanceh[i].ptsize==points) {
928 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
932 smooth=1; /* We will be adding a font - add it as smooth then */
933 } else { /* Smooth doesn't matter for this search */
934 for(i=0;i<TT_CHC;i++) {
935 if (handle->instanceh[i].ptsize == points
936 && handle->instanceh[i].smooth == smooth) {
937 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
943 /* Found the instance in the cache - return the cache index */
945 for(idx=0;idx<TT_CHC;idx++) {
946 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
949 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
950 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
951 USTRCT(handle->instanceh[idx].instance) ));
953 if ( USTRCT(handle->instanceh[idx].instance) ) {
954 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
956 /* Free cached glyphs */
958 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
959 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
962 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
963 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
966 /* Free instance if needed */
967 TT_Done_Instance( handle->instanceh[idx].instance );
970 /* create and initialize instance */
971 /* FIXME: probably a memory leak on fail */
973 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
974 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
975 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
978 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
982 /* Now that the instance should the inplace we need to lower all of the
983 ru counts and put `this' one with the highest entry */
985 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
987 handle->instanceh[idx].order=TT_CHC-1;
988 handle->instanceh[idx].ptsize=points;
989 handle->instanceh[idx].smooth=smooth;
990 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
992 /* Zero the memory for the glyph storage so they are not thought as
993 cached if they haven't been cached since this new font was loaded */
996 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
997 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
1005 =item i_tt_new(fontname)
1007 Creates a new font handle object, finds a character map and initialise the
1008 the font handle's cache
1010 fontname - path to the font to load
1016 i_tt_new(const char *fontname) {
1018 TT_Fonthandle *handle;
1020 unsigned short platform,encoding;
1024 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1026 /* allocate memory for the structure */
1028 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
1030 /* load the typeface */
1031 error = TT_Open_Face( engine, fontname, &handle->face );
1033 if ( error == TT_Err_Could_Not_Open_File ) {
1034 mm_log((1, "Could not find/open %s.\n", fontname ));
1037 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1040 i_tt_push_error(error);
1044 TT_Get_Face_Properties( handle->face, &(handle->properties) );
1046 /* First, look for a Unicode charmap */
1047 n = handle->properties.num_CharMaps;
1048 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1050 for ( i = 0; i < n; i++ ) {
1051 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
1052 if ( (platform == 3 && encoding == 1 )
1053 || (platform == 0 && encoding == 0 ) ) {
1054 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1055 platform, encoding));
1056 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1060 if (!USTRCT(handle->char_map) && n != 0) {
1061 /* just use the first one */
1062 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1065 /* Zero the pointsizes - and ordering */
1067 for(i=0;i<TT_CHC;i++) {
1068 USTRCT(handle->instanceh[i].instance)=NULL;
1069 handle->instanceh[i].order=i;
1070 handle->instanceh[i].ptsize=0;
1071 handle->instanceh[i].smooth=-1;
1075 handle->loaded_names = 0;
1078 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1085 * raster map management
1089 =item i_tt_init_raster_map(bit, width, height, smooth)
1091 Allocates internal memory for the bitmap as needed by the parameters (internal)
1093 bit - bitmap to allocate into
1094 width - width of the bitmap
1095 height - height of the bitmap
1096 smooth - boolean (True: antialias on, False: antialias is off)
1103 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1105 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1108 bit->width = ( width + 3 ) & -4;
1109 bit->flow = TT_Flow_Down;
1112 bit->cols = bit->width;
1113 bit->size = bit->rows * bit->width;
1115 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1116 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1119 /* rows can be 0 for some glyphs, for example ' ' */
1120 if (bit->rows && bit->size / bit->rows != bit->cols) {
1121 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1122 bit->width, bit->rows);
1125 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 ));
1127 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1128 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1133 =item i_tt_clear_raster_map(bit)
1135 Frees the bitmap data and sets pointer to NULL (internal)
1137 bit - bitmap to free
1144 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1145 myfree( bit->bitmap );
1151 =item i_tt_clear_raster_map(bit)
1153 Clears the specified bitmap (internal)
1155 bit - bitmap to zero
1163 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1164 memset( bit->bitmap, 0, bit->size );
1169 =item i_tt_blit_or(dst, src, x_off, y_off)
1171 function that blits one raster map into another (internal)
1173 dst - destination bitmap
1175 x_off - x offset into the destination bitmap
1176 y_off - y offset into the destination bitmap
1183 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1186 unsigned char *s, *d;
1188 x1 = x_off < 0 ? -x_off : 0;
1189 y1 = y_off < 0 ? -y_off : 0;
1191 x2 = (int)dst->cols - x_off;
1192 if ( x2 > src->cols ) x2 = src->cols;
1194 y2 = (int)dst->rows - y_off;
1195 if ( y2 > src->rows ) y2 = src->rows;
1197 if ( x1 >= x2 ) return;
1199 /* do the real work now */
1201 for ( y = y1; y < y2; ++y ) {
1202 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
1203 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1205 for ( x = x1; x < x2; ++x ) {
1214 /* useful for debugging */
1217 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1219 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1220 for (y = 0; y < bit->rows; ++y) {
1221 fprintf(out, "%2d:", y);
1222 for (x = 0; x < bit->cols; ++x) {
1223 if ((x & 7) == 0 && x) putc(' ', out);
1224 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1233 =item i_tt_get_glyph(handle, inst, j)
1235 Function to see if a glyph exists and if so cache it (internal)
1237 handle - pointer to font handle
1238 inst - font instance
1239 j - charcode of glyph
1246 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1247 unsigned short load_flags, code;
1250 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1251 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1253 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1255 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1256 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1257 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1261 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1262 /* clean up the entry */
1263 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1264 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1265 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1268 /* Ok - it wasn't cached - try to get it in */
1269 load_flags = TTLOAD_SCALE_GLYPH;
1270 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1272 if ( !TT_VALID(handle->char_map) ) {
1273 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1274 if ( code >= handle->properties.num_Glyphs ) code = 0;
1275 } else code = TT_Char_Index( handle->char_map, j );
1277 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1278 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1279 i_push_error(error, "TT_New_Glyph()");
1282 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1283 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1285 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1286 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1287 i_push_error(error, "TT_Load_Glyph()");
1291 /* At this point the glyph should be allocated and loaded */
1292 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1294 /* Next get the glyph metrics */
1295 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1296 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1298 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1299 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1300 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1301 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1302 i_push_error(error, "TT_Get_Glyph_Metrics()");
1310 =item i_tt_has_chars(handle, text, len, utf8, out)
1312 Check if the given characters are defined by the font. Note that len
1313 is the number of bytes, not the number of characters (when utf8 is
1316 Returns the number of characters that were checked.
1322 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1325 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1326 handle, text, len, utf8));
1332 c = i_utf8_advance(&text, &len);
1334 i_push_error(0, "invalid UTF8 character");
1339 c = (unsigned char)*text++;
1343 if (TT_VALID(handle->char_map)) {
1344 index = TT_Char_Index(handle->char_map, c);
1347 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1348 if (index >= handle->properties.num_Glyphs)
1351 *out++ = index != 0;
1359 =item i_tt_destroy(handle)
1361 Clears the data taken by a font including all cached data such as
1364 handle - pointer to font handle
1370 i_tt_destroy( TT_Fonthandle *handle) {
1371 TT_Close_Face( handle->face );
1374 /* FIXME: Should these be freed automatically by the library?
1376 TT_Done_Instance( instance );
1378 i_tt_done_glyphs( void ) {
1381 if ( !glyphs ) return;
1383 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1393 * FreeType Rendering functions
1398 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1400 Renders a single glyph into the bit rastermap (internal)
1402 handle - pointer to font handle
1403 gmetrics - the metrics for the glyph to be rendered
1404 bit - large bitmap that is the destination for the text
1405 smallbit - small bitmap that is used only if smooth is true
1406 x_off - x offset of glyph
1407 y_off - y offset of glyph
1408 smooth - boolean (True: antialias on, False: antialias is off)
1415 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 ) {
1417 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",
1418 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1420 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1422 TT_F26Dot6 xmin, ymin, xmax, ymax;
1424 xmin = gmetrics->bbox.xMin & -64;
1425 ymin = gmetrics->bbox.yMin & -64;
1426 xmax = (gmetrics->bbox.xMax + 63) & -64;
1427 ymax = (gmetrics->bbox.yMax + 63) & -64;
1429 i_tt_clear_raster_map( small_bit );
1430 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1431 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1437 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1439 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1441 handle - pointer to font handle
1442 inst - font instance
1443 bit - large bitmap that is the destination for the text
1444 smallbit - small bitmap that is used only if smooth is true
1445 txt - string to render
1446 len - length of the string to render
1447 smooth - boolean (True: antialias on, False: antialias is off)
1454 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1455 TT_Raster_Map *small_bit, int cords[6],
1456 char const* txt, int len, int smooth, int utf8 ) {
1460 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",
1461 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1464 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1467 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 */
1472 j = i_utf8_advance(&txt, &len);
1474 i_push_error(0, "invalid UTF8 character");
1479 j = (unsigned char)*txt++;
1482 if ( !i_tt_get_glyph(handle,inst,j) )
1484 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1485 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1486 small_bit, x, y, smooth );
1487 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1495 * Functions to render rasters (single channel images) onto images
1499 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1501 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1503 im - image to dump raster on
1504 bit - bitmap that contains the text to be dumped to im
1505 xb, yb - coordinates, left edge and baseline
1506 cl - color to use for text
1507 smooth - boolean (True: antialias on, False: antialias is off)
1514 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
1515 unsigned char *bmap;
1517 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));
1524 i_render_init(&r, im, bit->cols);
1525 for(y=0;y<bit->rows;y++) {
1527 for(x=0;x<bit->width;x++) {
1528 c = (unsigned char)bmap[y*(bit->cols)+x];
1530 i_gpix(im,x+xb,y+yb,&val);
1531 for(ch=0;ch<im->channels;ch++)
1532 val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255;
1533 i_ppix(im,x+xb,y+yb,&val);
1536 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
1541 for(y=0;y<bit->rows;y++) {
1542 unsigned mask = 0x80;
1543 unsigned char *p = bmap + y * bit->cols;
1545 for(x = 0; x < bit->width; x++) {
1547 i_ppix(im, x+xb, y+yb, cl);
1562 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1564 Function to dump a raster onto a single channel image in color (internal)
1566 im - image to dump raster on
1567 bit - bitmap that contains the text to be dumped to im
1568 xb, yb - coordinates, left edge and baseline
1569 channel - channel to copy to
1570 smooth - boolean (True: antialias on, False: antialias is off)
1577 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1578 unsigned char *bmap;
1581 int old_mask = im->ch_mask;
1582 im->ch_mask = 1 << channel;
1584 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));
1589 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1590 c = bmap[y*(bit->cols)+x];
1591 val.channel[channel] = c;
1592 i_ppix(im,x+xb,y+yb,&val);
1595 for(y=0;y<bit->rows;y++) {
1596 unsigned mask = 0x80;
1597 unsigned char *p = bmap + y * bit->cols;
1599 for(x=0;x<bit->width;x++) {
1600 val.channel[channel] = (*p & mask) ? 255 : 0;
1601 i_ppix(im,x+xb,y+yb,&val);
1611 im->ch_mask = old_mask;
1616 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1618 interface for generating single channel raster of text (internal)
1620 handle - pointer to font handle
1621 bit - the bitmap that is allocated, rendered into and NOT freed
1622 cords - the bounding box (modified in place)
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)
1633 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1636 TT_Raster_Map small_bit;
1638 /* find or install an instance */
1639 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1640 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1644 /* calculate bounding box */
1645 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1649 width = cords[2]-cords[0];
1650 height = cords[5]-cords[4];
1652 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1654 i_tt_init_raster_map ( bit, width, height, smooth );
1655 i_tt_clear_raster_map( bit );
1656 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1658 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1661 i_tt_done_raster_map( &small_bit );
1665 if ( smooth ) i_tt_done_raster_map( &small_bit );
1672 * Exported text rendering interfaces
1677 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1679 Interface to text rendering into a single channel in an image
1681 handle - pointer to font handle
1682 im - image to render text on to
1683 xb, yb - coordinates, left edge and baseline
1684 channel - channel to render into
1685 points - font size to use
1686 txt - string to render
1687 len - length of the string to render
1688 smooth - boolean (True: antialias on, False: antialias is off)
1694 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 ) {
1696 int cords[BOUNDING_BOX_COUNT];
1697 int ascent, st_offset, y;
1701 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1703 ascent=cords[BBOX_ASCENT];
1704 st_offset=cords[BBOX_NEG_WIDTH];
1705 y = align ? yb-ascent : yb;
1707 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1708 i_tt_done_raster_map( &bit );
1715 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1717 Interface to text rendering in a single color onto an image
1719 handle - pointer to font handle
1720 im - image to render text on to
1721 xb, yb - coordinates, left edge and baseline
1722 cl - color to use for text
1723 points - font size to use
1724 txt - string to render
1725 len - length of the string to render
1726 smooth - boolean (True: antialias on, False: antialias is off)
1732 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) {
1733 int cords[BOUNDING_BOX_COUNT];
1734 int ascent, st_offset, y;
1739 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1741 ascent=cords[BBOX_ASCENT];
1742 st_offset=cords[BBOX_NEG_WIDTH];
1743 y = align ? yb-ascent : yb;
1745 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1746 i_tt_done_raster_map( &bit );
1753 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1755 Function to get texts bounding boxes given the instance of the font (internal)
1757 handle - pointer to font handle
1758 inst - font instance
1759 txt - string to measure
1760 len - length of the string to render
1761 cords - the bounding box (modified in place)
1768 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1769 int upm, casc, cdesc, first;
1780 unsigned char *ustr;
1781 ustr=(unsigned char*)txt;
1783 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));
1785 upm = handle->properties.header->Units_Per_EM;
1786 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1787 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1792 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1797 j = i_utf8_advance(&txt, &len);
1799 i_push_error(0, "invalid UTF8 character");
1804 j = (unsigned char)*txt++;
1807 if ( i_tt_get_glyph(handle,inst,j) ) {
1808 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1809 width += gm->advance / 64;
1810 casc = (gm->bbox.yMax+63) / 64;
1811 cdesc = (gm->bbox.yMin-63) / 64;
1813 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1814 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1817 start = gm->bbox.xMin / 64;
1818 ascent = (gm->bbox.yMax+63) / 64;
1819 descent = (gm->bbox.yMin-63) / 64;
1822 if (!len) { /* if at end of string */
1823 /* the right-side bearing - in case the right-side of a
1824 character goes past the right of the advance width,
1825 as is common for italic fonts
1827 rightb = gm->advance - gm->bearingX
1828 - (gm->bbox.xMax - gm->bbox.xMin);
1829 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1830 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1833 ascent = (ascent > casc ? ascent : casc );
1834 descent = (descent < cdesc ? descent : cdesc);
1838 cords[BBOX_NEG_WIDTH]=start;
1839 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1840 cords[BBOX_POS_WIDTH]=width;
1842 cords[BBOX_POS_WIDTH] -= rightb / 64;
1843 cords[BBOX_GLOBAL_ASCENT]=gascent;
1844 cords[BBOX_DESCENT]=descent;
1845 cords[BBOX_ASCENT]=ascent;
1846 cords[BBOX_ADVANCE_WIDTH] = width;
1847 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1849 return BBOX_RIGHT_BEARING + 1;
1854 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1856 Interface to get a strings bounding box
1858 handle - pointer to font handle
1859 points - font size to use
1860 txt - string to render
1861 len - length of the string to render
1862 cords - the bounding box (modified in place)
1868 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8) {
1872 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1874 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1875 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1876 mm_log((1,"i_tt_text: get instance failed\n"));
1880 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1884 =item i_tt_face_name(handle, name_buf, name_buf_size)
1886 Retrieve's the font's postscript name.
1888 This is complicated by the need to handle encodings and so on.
1893 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1894 TT_Face_Properties props;
1897 TT_UShort platform_id, encoding_id, lang_id, name_id;
1900 int want_index = -1; /* an acceptable but not perfect name */
1905 TT_Get_Face_Properties(handle->face, &props);
1906 name_count = props.num_Names;
1907 for (i = 0; i < name_count; ++i) {
1908 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1911 TT_Get_Name_String(handle->face, i, &name, &name_len);
1913 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1914 && name_id == TT_NAME_ID_PS_NAME) {
1915 int might_want_index = -1;
1916 int might_score = 0;
1917 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1919 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1920 /* exactly what we want */
1925 if (platform_id == TT_PLATFORM_MICROSOFT
1926 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1927 /* any english is good */
1928 might_want_index = i;
1931 /* there might be something in between */
1933 /* anything non-unicode is better than nothing */
1934 might_want_index = i;
1937 if (might_score > score) {
1938 score = might_score;
1939 want_index = might_want_index;
1944 if (want_index != -1) {
1945 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1947 strncpy(name_buf, name, name_buf_size);
1948 name_buf[name_buf_size-1] = '\0';
1950 return strlen(name) + 1;
1953 i_push_error(0, "no face name present");
1958 void i_tt_dump_names(TT_Fonthandle *handle) {
1959 TT_Face_Properties props;
1962 TT_UShort platform_id, encoding_id, lang_id, name_id;
1966 TT_Get_Face_Properties(handle->face, &props);
1967 name_count = props.num_Names;
1968 for (i = 0; i < name_count; ++i) {
1969 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1971 TT_Get_Name_String(handle->face, i, &name, &name_len);
1973 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1974 encoding_id, lang_id, name_id);
1975 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1976 printf("(unicode)\n");
1979 printf("'%s'\n", name);
1986 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1987 size_t name_buf_size) {
1995 if (!handle->loaded_names) {
1997 mm_log((1, "Loading PS Names"));
1998 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
1999 ++handle->loaded_names;
2002 if (handle->load_cond) {
2003 i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
2007 index = TT_Char_Index(handle->char_map, ch);
2009 i_push_error(0, "no such character");
2013 rc = TT_Get_PS_Name(handle->face, index, &psname);
2016 i_push_error(rc, "error getting name");
2020 strncpy(name_buf, psname, name_buf_size);
2021 name_buf[name_buf_size-1] = '\0';
2023 return strlen(psname) + 1;
2025 mm_log((1, "FTXPOST extension not enabled\n"));
2027 i_push_error(0, "Use of FTXPOST extension disabled");
2034 =item i_tt_push_error(code)
2036 Push an error message and code onto the Imager error stack.
2041 i_tt_push_error(TT_Error rc) {
2043 TT_String const *msg = TT_ErrToString18(rc);
2045 i_push_error(rc, msg);
2047 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
2051 #endif /* HAVE_LIBTT */
2059 Arnar M. Hrafnkelsson <addi@umich.edu>