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) {
410 int xsize,ysize,x,y,ch;
413 int mod_flags = t1_get_flags(flags);
416 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
420 char *work = t1_from_utf8(str, len, &worklen);
421 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
425 /* T1_AASetString() accepts a char * not a const char */
426 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
431 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
432 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
433 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
434 mm_log((1,"bpp: %d\n",glyph->bpp));
436 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
437 ysize=glyph->metrics.ascent-glyph->metrics.descent;
439 mm_log((1,"width: %d height: %d\n",xsize,ysize));
441 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
443 i_render_init(&r, im, xsize);
444 for(y=0;y<ysize;y++) {
445 i_render_color(&r, xb, yb+y, xsize, glyph->bits+y*xsize, cl);
453 =item t1_get_flags(flags)
455 Processes the characters in I<flags> to create a mod_flags value used
456 by some T1Lib functions.
461 t1_get_flags(char const *flags) {
462 int mod_flags = T1_KERNING;
466 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
467 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
468 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
469 /* ignore anything we don't recognize */
477 =item t1_from_utf8(char const *in, int len, int *outlen)
479 Produces an unencoded version of I<in> by dropping any Unicode
482 Returns a newly allocated buffer which should be freed with myfree().
483 Sets *outlen to the number of bytes used in the output string.
489 t1_from_utf8(char const *in, int len, int *outlen) {
490 /* at this point len is from a perl SV, so can't approach MAXINT */
491 char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
496 c = i_utf8_advance(&in, &len);
499 i_push_error(0, "invalid UTF8 character");
502 /* yeah, just drop them */
514 =item i_t1_has_chars(font_num, text, len, utf8, out)
516 Check if the given characters are defined by the font. Note that len
517 is the number of bytes, not the number of characters (when utf8 is
520 out[char index] will be true if the character exists.
522 Accepts UTF-8, but since T1 can only have 256 characters, any chars
523 with values over 255 will simply be returned as false.
525 Returns the number of characters that were checked.
531 i_t1_has_chars(int font_num, const char *text, int len, int utf8,
535 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
536 font_num, text, len, utf8));
539 if (T1_LoadFont(font_num)) {
547 c = i_utf8_advance(&text, &len);
549 i_push_error(0, "invalid UTF8 character");
554 c = (unsigned char)*text++;
559 /* limit of 256 characters for T1 */
563 char const * name = T1_GetCharName(font_num, (unsigned char)c);
566 *out++ = strcmp(name, ".notdef") != 0;
569 mm_log((2, " No name found for character %lx\n", c));
580 =item i_t1_face_name(font_num, name_buf, name_buf_size)
582 Copies the face name of the given C<font_num> to C<name_buf>. Returns
583 the number of characters required to store the name (which can be
584 larger than C<name_buf_size>, including the space required to store
585 the terminating NUL).
587 If name_buf is too small (as specified by name_buf_size) then the name
588 will be truncated. name_buf will always be NUL termintaed.
594 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
598 if (T1_LoadFont(font_num)) {
602 name = T1_GetFontName(font_num);
605 strncpy(name_buf, name, name_buf_size);
606 name_buf[name_buf_size-1] = '\0';
607 return strlen(name) + 1;
616 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
617 size_t name_buf_size) {
624 if (T1_LoadFont(font_num)) {
628 name = T1_GetCharName(font_num, (unsigned char)ch);
630 if (strcmp(name, ".notdef")) {
631 strncpy(name_buf, name, name_buf_size);
632 name_buf[name_buf_size-1] = '\0';
633 return strlen(name) + 1;
646 t1_push_error(void) {
649 i_push_error(0, "No error");
652 #ifdef T1ERR_SCAN_FONT_FORMAT
653 case T1ERR_SCAN_FONT_FORMAT:
654 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
658 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
659 case T1ERR_SCAN_FILE_OPEN_ERR:
660 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
664 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
665 case T1ERR_SCAN_OUT_OF_MEMORY:
666 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
670 #ifdef T1ERR_SCAN_ERROR
671 case T1ERR_SCAN_ERROR:
672 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
676 #ifdef T1ERR_SCAN_FILE_EOF
677 case T1ERR_SCAN_FILE_EOF:
678 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
682 #ifdef T1ERR_PATH_ERROR
683 case T1ERR_PATH_ERROR:
684 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
688 #ifdef T1ERR_PARSE_ERROR
689 case T1ERR_PARSE_ERROR:
690 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
694 #ifdef T1ERR_TYPE1_ABORT
695 case T1ERR_TYPE1_ABORT:
696 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
700 #ifdef T1ERR_INVALID_FONTID
701 case T1ERR_INVALID_FONTID:
702 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
706 #ifdef T1ERR_INVALID_PARAMETER
707 case T1ERR_INVALID_PARAMETER:
708 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
712 #ifdef T1ERR_OP_NOT_PERMITTED
713 case T1ERR_OP_NOT_PERMITTED:
714 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
718 #ifdef T1ERR_ALLOC_MEM
719 case T1ERR_ALLOC_MEM:
720 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
724 #ifdef T1ERR_FILE_OPEN_ERR
725 case T1ERR_FILE_OPEN_ERR:
726 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
730 #ifdef T1ERR_UNSPECIFIED
731 case T1ERR_UNSPECIFIED:
732 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
736 #ifdef T1ERR_NO_AFM_DATA
737 case T1ERR_NO_AFM_DATA:
738 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
744 i_push_error(T1ERR_X11, "X11");
748 #ifdef T1ERR_COMPOSITE_CHAR
749 case T1ERR_COMPOSITE_CHAR:
750 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
755 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
759 #endif /* HAVE_LIBT1 */
762 /* Truetype font support */
765 /* These are enabled by default when configuring Freetype 1.x
766 I haven't a clue how to reliably detect it at compile time.
768 We need a compilation probe in Makefile.PL
773 #include <freetype.h>
781 #include <ftxerr18.h>
784 /* some versions of FT1.x don't seem to define this - it's font defined
785 so it won't change */
786 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
787 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
790 /* convert a code point into an index in the glyph cache */
791 #define TT_HASH(x) ((x) & 0xFF)
793 typedef struct i_glyph_entry_ {
798 #define TT_NOCHAR (~0UL)
800 struct TT_Instancehandle_ {
801 TT_Instance instance;
802 TT_Instance_Metrics imetrics;
803 TT_Glyph_Metrics gmetrics[256];
804 i_tt_glyph_entry glyphs[256];
810 typedef struct TT_Instancehandle_ TT_Instancehandle;
812 struct TT_Fonthandle_ {
814 TT_Face_Properties properties;
815 TT_Instancehandle instanceh[TT_CHC];
825 #define USTRCT(x) ((x).z)
826 #define TT_VALID( handle ) ( ( handle ).z != NULL )
828 static void i_tt_push_error(TT_Error rc);
832 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
833 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
834 static void i_tt_done_raster_map( TT_Raster_Map *bit );
835 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
836 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
837 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
839 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
840 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
841 int x_off, int y_off, int smooth );
843 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
844 TT_Raster_Map *small_bit, int cords[6],
845 char const* txt, int len, int smooth, int utf8 );
846 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
847 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
849 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
850 float points, char const* txt, int len, int smooth, int utf8 );
851 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
854 /* static globals needed */
856 static TT_Engine engine;
857 static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
858 static int LTT_hinted = 1; /* FIXME: this too */
869 Initializes the freetype font rendering engine
877 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
879 mm_log((1,"init_tt()\n"));
880 error = TT_Init_FreeType( &engine );
882 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
887 error = TT_Init_Post_Extension( engine );
889 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
894 error = TT_Set_Raster_Gray_Palette(engine, palette);
896 mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
905 =item i_tt_get_instance(handle, points, smooth)
907 Finds a points+smooth instance or if one doesn't exist in the cache
908 allocates room and returns its cache entry
910 fontname - path to the font to load
911 handle - handle to the font.
912 points - points of the requested font
913 smooth - boolean (True: antialias on, False: antialias is off)
920 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
924 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
925 handle,points,smooth));
927 if (smooth == -1) { /* Smooth doesn't matter for this search */
928 for(i=0;i<TT_CHC;i++) {
929 if (handle->instanceh[i].ptsize==points) {
930 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
934 smooth=1; /* We will be adding a font - add it as smooth then */
935 } else { /* Smooth doesn't matter for this search */
936 for(i=0;i<TT_CHC;i++) {
937 if (handle->instanceh[i].ptsize == points
938 && handle->instanceh[i].smooth == smooth) {
939 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
945 /* Found the instance in the cache - return the cache index */
947 for(idx=0;idx<TT_CHC;idx++) {
948 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
951 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
952 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
953 USTRCT(handle->instanceh[idx].instance) ));
955 if ( USTRCT(handle->instanceh[idx].instance) ) {
956 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
958 /* Free cached glyphs */
960 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
961 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
964 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
965 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
968 /* Free instance if needed */
969 TT_Done_Instance( handle->instanceh[idx].instance );
972 /* create and initialize instance */
973 /* FIXME: probably a memory leak on fail */
975 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
976 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
977 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
980 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
984 /* Now that the instance should the inplace we need to lower all of the
985 ru counts and put `this' one with the highest entry */
987 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
989 handle->instanceh[idx].order=TT_CHC-1;
990 handle->instanceh[idx].ptsize=points;
991 handle->instanceh[idx].smooth=smooth;
992 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
994 /* Zero the memory for the glyph storage so they are not thought as
995 cached if they haven't been cached since this new font was loaded */
998 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
999 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
1007 =item i_tt_new(fontname)
1009 Creates a new font handle object, finds a character map and initialise the
1010 the font handle's cache
1012 fontname - path to the font to load
1018 i_tt_new(const char *fontname) {
1020 TT_Fonthandle *handle;
1022 unsigned short platform,encoding;
1026 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1028 /* allocate memory for the structure */
1030 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
1032 /* load the typeface */
1033 error = TT_Open_Face( engine, fontname, &handle->face );
1035 if ( error == TT_Err_Could_Not_Open_File ) {
1036 mm_log((1, "Could not find/open %s.\n", fontname ));
1039 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1042 i_tt_push_error(error);
1046 TT_Get_Face_Properties( handle->face, &(handle->properties) );
1048 /* First, look for a Unicode charmap */
1049 n = handle->properties.num_CharMaps;
1050 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1052 for ( i = 0; i < n; i++ ) {
1053 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
1054 if ( (platform == 3 && encoding == 1 )
1055 || (platform == 0 && encoding == 0 ) ) {
1056 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1057 platform, encoding));
1058 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1062 if (!USTRCT(handle->char_map) && n != 0) {
1063 /* just use the first one */
1064 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1067 /* Zero the pointsizes - and ordering */
1069 for(i=0;i<TT_CHC;i++) {
1070 USTRCT(handle->instanceh[i].instance)=NULL;
1071 handle->instanceh[i].order=i;
1072 handle->instanceh[i].ptsize=0;
1073 handle->instanceh[i].smooth=-1;
1077 handle->loaded_names = 0;
1080 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1087 * raster map management
1091 =item i_tt_init_raster_map(bit, width, height, smooth)
1093 Allocates internal memory for the bitmap as needed by the parameters (internal)
1095 bit - bitmap to allocate into
1096 width - width of the bitmap
1097 height - height of the bitmap
1098 smooth - boolean (True: antialias on, False: antialias is off)
1105 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1107 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1110 bit->width = ( width + 3 ) & -4;
1111 bit->flow = TT_Flow_Down;
1114 bit->cols = bit->width;
1115 bit->size = bit->rows * bit->width;
1117 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1118 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1121 /* rows can be 0 for some glyphs, for example ' ' */
1122 if (bit->rows && bit->size / bit->rows != bit->cols) {
1123 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1124 bit->width, bit->rows);
1127 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 ));
1129 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1130 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1135 =item i_tt_clear_raster_map(bit)
1137 Frees the bitmap data and sets pointer to NULL (internal)
1139 bit - bitmap to free
1146 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1147 myfree( bit->bitmap );
1153 =item i_tt_clear_raster_map(bit)
1155 Clears the specified bitmap (internal)
1157 bit - bitmap to zero
1165 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1166 memset( bit->bitmap, 0, bit->size );
1171 =item i_tt_blit_or(dst, src, x_off, y_off)
1173 function that blits one raster map into another (internal)
1175 dst - destination bitmap
1177 x_off - x offset into the destination bitmap
1178 y_off - y offset into the destination bitmap
1185 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1188 unsigned char *s, *d;
1190 x1 = x_off < 0 ? -x_off : 0;
1191 y1 = y_off < 0 ? -y_off : 0;
1193 x2 = (int)dst->cols - x_off;
1194 if ( x2 > src->cols ) x2 = src->cols;
1196 y2 = (int)dst->rows - y_off;
1197 if ( y2 > src->rows ) y2 = src->rows;
1199 if ( x1 >= x2 ) return;
1201 /* do the real work now */
1203 for ( y = y1; y < y2; ++y ) {
1204 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
1205 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1207 for ( x = x1; x < x2; ++x ) {
1216 /* useful for debugging */
1219 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1221 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1222 for (y = 0; y < bit->rows; ++y) {
1223 fprintf(out, "%2d:", y);
1224 for (x = 0; x < bit->cols; ++x) {
1225 if ((x & 7) == 0 && x) putc(' ', out);
1226 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1235 =item i_tt_get_glyph(handle, inst, j)
1237 Function to see if a glyph exists and if so cache it (internal)
1239 handle - pointer to font handle
1240 inst - font instance
1241 j - charcode of glyph
1248 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1249 unsigned short load_flags, code;
1252 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1253 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1255 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1257 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1258 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1259 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1263 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1264 /* clean up the entry */
1265 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1266 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1267 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1270 /* Ok - it wasn't cached - try to get it in */
1271 load_flags = TTLOAD_SCALE_GLYPH;
1272 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1274 if ( !TT_VALID(handle->char_map) ) {
1275 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1276 if ( code >= handle->properties.num_Glyphs ) code = 0;
1277 } else code = TT_Char_Index( handle->char_map, j );
1279 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1280 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1281 i_push_error(error, "TT_New_Glyph()");
1284 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1285 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1287 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1288 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1289 i_push_error(error, "TT_Load_Glyph()");
1293 /* At this point the glyph should be allocated and loaded */
1294 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1296 /* Next get the glyph metrics */
1297 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1298 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1300 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1301 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1302 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1303 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1304 i_push_error(error, "TT_Get_Glyph_Metrics()");
1312 =item i_tt_has_chars(handle, text, len, utf8, out)
1314 Check if the given characters are defined by the font. Note that len
1315 is the number of bytes, not the number of characters (when utf8 is
1318 Returns the number of characters that were checked.
1324 i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
1327 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1328 handle, text, len, utf8));
1334 c = i_utf8_advance(&text, &len);
1336 i_push_error(0, "invalid UTF8 character");
1341 c = (unsigned char)*text++;
1345 if (TT_VALID(handle->char_map)) {
1346 index = TT_Char_Index(handle->char_map, c);
1349 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1350 if (index >= handle->properties.num_Glyphs)
1353 *out++ = index != 0;
1361 =item i_tt_destroy(handle)
1363 Clears the data taken by a font including all cached data such as
1366 handle - pointer to font handle
1372 i_tt_destroy( TT_Fonthandle *handle) {
1373 TT_Close_Face( handle->face );
1376 /* FIXME: Should these be freed automatically by the library?
1378 TT_Done_Instance( instance );
1380 i_tt_done_glyphs( void ) {
1383 if ( !glyphs ) return;
1385 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1395 * FreeType Rendering functions
1400 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1402 Renders a single glyph into the bit rastermap (internal)
1404 handle - pointer to font handle
1405 gmetrics - the metrics for the glyph to be rendered
1406 bit - large bitmap that is the destination for the text
1407 smallbit - small bitmap that is used only if smooth is true
1408 x_off - x offset of glyph
1409 y_off - y offset of glyph
1410 smooth - boolean (True: antialias on, False: antialias is off)
1417 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 ) {
1419 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",
1420 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1422 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1424 TT_F26Dot6 xmin, ymin, xmax, ymax;
1426 xmin = gmetrics->bbox.xMin & -64;
1427 ymin = gmetrics->bbox.yMin & -64;
1428 xmax = (gmetrics->bbox.xMax + 63) & -64;
1429 ymax = (gmetrics->bbox.yMax + 63) & -64;
1431 i_tt_clear_raster_map( small_bit );
1432 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1433 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1439 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1441 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1443 handle - pointer to font handle
1444 inst - font instance
1445 bit - large bitmap that is the destination for the text
1446 smallbit - small bitmap that is used only if smooth is true
1447 txt - string to render
1448 len - length of the string to render
1449 smooth - boolean (True: antialias on, False: antialias is off)
1456 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1457 TT_Raster_Map *small_bit, int cords[6],
1458 char const* txt, int len, int smooth, int utf8 ) {
1462 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",
1463 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1466 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1469 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 */
1474 j = i_utf8_advance(&txt, &len);
1476 i_push_error(0, "invalid UTF8 character");
1481 j = (unsigned char)*txt++;
1484 if ( !i_tt_get_glyph(handle,inst,j) )
1486 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1487 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1488 small_bit, x, y, smooth );
1489 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1497 * Functions to render rasters (single channel images) onto images
1501 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1503 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1505 im - image to dump raster on
1506 bit - bitmap that contains the text to be dumped to im
1507 xb, yb - coordinates, left edge and baseline
1508 cl - color to use for text
1509 smooth - boolean (True: antialias on, False: antialias is off)
1516 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
1517 unsigned char *bmap;
1520 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));
1527 i_render_init(&r, im, bit->cols);
1528 for(y=0;y<bit->rows;y++) {
1530 for(x=0;x<bit->width;x++) {
1531 c = (unsigned char)bmap[y*(bit->cols)+x];
1533 i_gpix(im,x+xb,y+yb,&val);
1534 for(ch=0;ch<im->channels;ch++)
1535 val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255;
1536 i_ppix(im,x+xb,y+yb,&val);
1539 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
1544 for(y=0;y<bit->rows;y++) {
1545 unsigned mask = 0x80;
1546 unsigned char *p = bmap + y * bit->cols;
1548 for(x = 0; x < bit->width; x++) {
1550 i_ppix(im, x+xb, y+yb, cl);
1565 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1567 Function to dump a raster onto a single channel image in color (internal)
1569 im - image to dump raster on
1570 bit - bitmap that contains the text to be dumped to im
1571 xb, yb - coordinates, left edge and baseline
1572 channel - channel to copy to
1573 smooth - boolean (True: antialias on, False: antialias is off)
1580 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1584 int old_mask = im->ch_mask;
1585 im->ch_mask = 1 << channel;
1587 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 bmap = (char *)bit->bitmap;
1592 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1593 c = bmap[y*(bit->cols)+x];
1594 val.channel[channel] = c;
1595 i_ppix(im,x+xb,y+yb,&val);
1598 for(y=0;y<bit->rows;y++) {
1599 unsigned mask = 0x80;
1600 unsigned char *p = bmap + y * bit->cols;
1602 for(x=0;x<bit->width;x++) {
1603 val.channel[channel] = (*p & mask) ? 255 : 0;
1604 i_ppix(im,x+xb,y+yb,&val);
1614 im->ch_mask = old_mask;
1619 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1621 interface for generating single channel raster of text (internal)
1623 handle - pointer to font handle
1624 bit - the bitmap that is allocated, rendered into and NOT freed
1625 cords - the bounding box (modified in place)
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)
1636 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
1639 TT_Raster_Map small_bit;
1641 /* find or install an instance */
1642 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1643 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1647 /* calculate bounding box */
1648 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1652 width = cords[2]-cords[0];
1653 height = cords[5]-cords[4];
1655 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1657 i_tt_init_raster_map ( bit, width, height, smooth );
1658 i_tt_clear_raster_map( bit );
1659 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1661 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1664 i_tt_done_raster_map( &small_bit );
1668 if ( smooth ) i_tt_done_raster_map( &small_bit );
1675 * Exported text rendering interfaces
1680 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1682 Interface to text rendering into a single channel in an image
1684 handle - pointer to font handle
1685 im - image to render text on to
1686 xb, yb - coordinates, left edge and baseline
1687 channel - channel to render into
1688 points - font size to use
1689 txt - string to render
1690 len - length of the string to render
1691 smooth - boolean (True: antialias on, False: antialias is off)
1697 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 ) {
1699 int cords[BOUNDING_BOX_COUNT];
1700 int ascent, st_offset, y;
1704 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1706 ascent=cords[BBOX_ASCENT];
1707 st_offset=cords[BBOX_NEG_WIDTH];
1708 y = align ? yb-ascent : yb;
1710 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1711 i_tt_done_raster_map( &bit );
1718 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1720 Interface to text rendering in a single color onto an image
1722 handle - pointer to font handle
1723 im - image to render text on to
1724 xb, yb - coordinates, left edge and baseline
1725 cl - color to use for text
1726 points - font size to use
1727 txt - string to render
1728 len - length of the string to render
1729 smooth - boolean (True: antialias on, False: antialias is off)
1735 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) {
1736 int cords[BOUNDING_BOX_COUNT];
1737 int ascent, st_offset, y;
1742 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1744 ascent=cords[BBOX_ASCENT];
1745 st_offset=cords[BBOX_NEG_WIDTH];
1746 y = align ? yb-ascent : yb;
1748 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1749 i_tt_done_raster_map( &bit );
1756 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1758 Function to get texts bounding boxes given the instance of the font (internal)
1760 handle - pointer to font handle
1761 inst - font instance
1762 txt - string to measure
1763 len - length of the string to render
1764 cords - the bounding box (modified in place)
1771 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1772 int upm, casc, cdesc, first;
1783 unsigned char *ustr;
1784 ustr=(unsigned char*)txt;
1786 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));
1788 upm = handle->properties.header->Units_Per_EM;
1789 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1790 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1795 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1800 j = i_utf8_advance(&txt, &len);
1802 i_push_error(0, "invalid UTF8 character");
1807 j = (unsigned char)*txt++;
1810 if ( i_tt_get_glyph(handle,inst,j) ) {
1811 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1812 width += gm->advance / 64;
1813 casc = (gm->bbox.yMax+63) / 64;
1814 cdesc = (gm->bbox.yMin-63) / 64;
1816 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1817 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1820 start = gm->bbox.xMin / 64;
1821 ascent = (gm->bbox.yMax+63) / 64;
1822 descent = (gm->bbox.yMin-63) / 64;
1825 if (!len) { /* if at end of string */
1826 /* the right-side bearing - in case the right-side of a
1827 character goes past the right of the advance width,
1828 as is common for italic fonts
1830 rightb = gm->advance - gm->bearingX
1831 - (gm->bbox.xMax - gm->bbox.xMin);
1832 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1833 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1836 ascent = (ascent > casc ? ascent : casc );
1837 descent = (descent < cdesc ? descent : cdesc);
1841 cords[BBOX_NEG_WIDTH]=start;
1842 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1843 cords[BBOX_POS_WIDTH]=width;
1845 cords[BBOX_POS_WIDTH] -= rightb / 64;
1846 cords[BBOX_GLOBAL_ASCENT]=gascent;
1847 cords[BBOX_DESCENT]=descent;
1848 cords[BBOX_ASCENT]=ascent;
1849 cords[BBOX_ADVANCE_WIDTH] = width;
1850 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1852 return BBOX_RIGHT_BEARING + 1;
1857 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1859 Interface to get a strings bounding box
1861 handle - pointer to font handle
1862 points - font size to use
1863 txt - string to render
1864 len - length of the string to render
1865 cords - the bounding box (modified in place)
1871 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8) {
1875 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1877 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1878 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1879 mm_log((1,"i_tt_text: get instance failed\n"));
1883 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1887 =item i_tt_face_name(handle, name_buf, name_buf_size)
1889 Retrieve's the font's postscript name.
1891 This is complicated by the need to handle encodings and so on.
1896 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1897 TT_Face_Properties props;
1900 TT_UShort platform_id, encoding_id, lang_id, name_id;
1903 int want_index = -1; /* an acceptable but not perfect name */
1908 TT_Get_Face_Properties(handle->face, &props);
1909 name_count = props.num_Names;
1910 for (i = 0; i < name_count; ++i) {
1911 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1914 TT_Get_Name_String(handle->face, i, &name, &name_len);
1916 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1917 && name_id == TT_NAME_ID_PS_NAME) {
1918 int might_want_index = -1;
1919 int might_score = 0;
1920 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1922 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1923 /* exactly what we want */
1928 if (platform_id == TT_PLATFORM_MICROSOFT
1929 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1930 /* any english is good */
1931 might_want_index = i;
1934 /* there might be something in between */
1936 /* anything non-unicode is better than nothing */
1937 might_want_index = i;
1940 if (might_score > score) {
1941 score = might_score;
1942 want_index = might_want_index;
1947 if (want_index != -1) {
1948 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1950 strncpy(name_buf, name, name_buf_size);
1951 name_buf[name_buf_size-1] = '\0';
1953 return strlen(name) + 1;
1956 i_push_error(0, "no face name present");
1961 void i_tt_dump_names(TT_Fonthandle *handle) {
1962 TT_Face_Properties props;
1965 TT_UShort platform_id, encoding_id, lang_id, name_id;
1969 TT_Get_Face_Properties(handle->face, &props);
1970 name_count = props.num_Names;
1971 for (i = 0; i < name_count; ++i) {
1972 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1974 TT_Get_Name_String(handle->face, i, &name, &name_len);
1976 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1977 encoding_id, lang_id, name_id);
1978 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1979 printf("(unicode)\n");
1982 printf("'%s'\n", name);
1989 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
1990 size_t name_buf_size) {
1998 if (!handle->loaded_names) {
2000 mm_log((1, "Loading PS Names"));
2001 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
2002 ++handle->loaded_names;
2005 if (handle->load_cond) {
2006 i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
2010 index = TT_Char_Index(handle->char_map, ch);
2012 i_push_error(0, "no such character");
2016 rc = TT_Get_PS_Name(handle->face, index, &psname);
2019 i_push_error(rc, "error getting name");
2023 strncpy(name_buf, psname, name_buf_size);
2024 name_buf[name_buf_size-1] = '\0';
2026 return strlen(psname) + 1;
2028 mm_log((1, "FTXPOST extension not enabled\n"));
2030 i_push_error(0, "Use of FTXPOST extension disabled");
2037 =item i_tt_push_error(code)
2039 Push an error message and code onto the Imager error stack.
2044 i_tt_push_error(TT_Error rc) {
2046 TT_String const *msg = TT_ErrToString18(rc);
2048 i_push_error(rc, msg);
2050 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
2054 #endif /* HAVE_LIBTT */
2062 Arnar M. Hrafnkelsson <addi@umich.edu>