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"));
70 return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
78 static int t1_get_flags(char const *flags);
79 static char *t1_from_utf8(char const *in, size_t len, int *outlen);
81 static void t1_push_error(void);
83 static int t1_active_fonts = 0;
84 static int t1_initialized = 0;
87 =item i_init_t1(t1log)
89 Initializes the t1lib font rendering engine.
95 i_init_t1(int t1log) {
96 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
97 mm_log((1,"init_t1()\n"));
101 if (t1_active_fonts) {
102 mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
103 i_push_error(0, "Cannot re-initialize T1 - active fonts");
107 if (t1_initialized) {
112 init_flags |= LOGFILE;
113 if ((T1_InitLib(init_flags) == NULL)){
114 mm_log((1,"Initialization of t1lib failed\n"));
115 i_push_error(0, "T1_InitLib failed");
118 T1_SetLogLevel(T1LOG_DEBUG);
119 i_t1_set_aa(1); /* Default Antialias value */
130 Shuts the t1lib font rendering engine down.
132 This it seems that this function is never used.
145 =item i_t1_new(pfb, afm)
147 Loads the fonts with the given filenames, returns its font id
149 pfb - path to pfb file for font
150 afm - path to afm file for font
156 i_t1_new(char *pfb,char *afm) {
161 if (!t1_initialized && i_init_t1(0))
164 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
165 font_id = T1_AddFont(pfb);
167 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
172 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
173 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
182 =item i_t1_destroy(font_id)
184 Frees resources for a t1 font with given font id.
186 font_id - number of the font to free
192 i_t1_destroy(int font_id) {
193 mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
197 return T1_DeleteFont(font_id);
202 =item i_t1_set_aa(st)
204 Sets the antialiasing level of the t1 library.
206 st - 0 = NONE, 1 = LOW, 2 = HIGH.
212 i_t1_set_aa(int st) {
214 unsigned long cst[17];
217 T1_AASetBitsPerPixel( 8 );
218 T1_AASetLevel( T1_AA_NONE );
219 T1_AANSetGrayValues( 0, 255 );
220 mm_log((1,"setting T1 antialias to none\n"));
223 T1_AASetBitsPerPixel( 8 );
224 T1_AASetLevel( T1_AA_LOW );
225 T1_AASetGrayValues( 0,65,127,191,255 );
226 mm_log((1,"setting T1 antialias to low\n"));
229 T1_AASetBitsPerPixel(8);
230 T1_AASetLevel(T1_AA_HIGH);
231 for(i=0;i<17;i++) cst[i]=(i*255)/16;
232 T1_AAHSetGrayValues( cst );
233 mm_log((1,"setting T1 antialias to high\n"));
239 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
241 Interface to text rendering into a single channel in an image
243 im pointer to image structure
244 xb x coordinate of start of string
245 yb y coordinate of start of string ( see align )
246 channel - destination channel
247 fontnum - t1 library font id
248 points - number of points in fontheight
249 str - string to render
251 align - (0 - top of font glyph | 1 - baseline )
257 i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,size_t len,int align, int utf8, char const *flags) {
261 int mod_flags = t1_get_flags(flags);
263 unsigned int ch_mask_store;
265 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
269 char *work = t1_from_utf8(str, len, &worklen);
270 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
274 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
279 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
280 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
281 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
282 mm_log((1,"bpp: %d\n",glyph->bpp));
284 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
285 ysize=glyph->metrics.ascent-glyph->metrics.descent;
287 mm_log((1,"width: %d height: %d\n",xsize,ysize));
289 ch_mask_store=im->ch_mask;
290 im->ch_mask=1<<channel;
292 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
294 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
295 val.channel[channel]=glyph->bits[y*xsize+x];
296 i_ppix(im,x+xb,y+yb,&val);
299 im->ch_mask=ch_mask_store;
304 t1_fix_bbox(BBox *bbox, const char *str, size_t len, int advance,
305 int space_position) {
306 /* never called with len == 0 */
307 if (str[0] == space_position && bbox->llx > 0)
309 if (str[len-1] == space_position && bbox->urx < advance)
311 if (bbox->lly > bbox->ury)
312 bbox->lly = bbox->ury = 0;
316 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
318 function to get a strings bounding box given the font id and sizes
320 handle - pointer to font handle
321 fontnum - t1 library font id
322 points - number of points in fontheight
323 str - string to measure
325 cords - the bounding box (modified in place)
331 i_t1_bbox(int fontnum,float points,const char *str,size_t len,int cords[6], int utf8,char const *flags) {
334 int mod_flags = t1_get_flags(flags);
336 int space_position = T1_GetEncodingIndex(fontnum, "space");
338 mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
339 T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */
342 /* len == 0 has special meaning to T1lib, but it means there's
343 nothing to draw, so return that */
344 bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
350 char *work = t1_from_utf8(str, len, &worklen);
351 advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
352 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
353 t1_fix_bbox(&bbox, work, worklen, advance, space_position);
357 advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
358 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
359 t1_fix_bbox(&bbox, str, len, advance, space_position);
362 gbbox = T1_GetFontBBox(fontnum);
364 mm_log((1,"bbox: (%d,%d,%d,%d)\n",
365 (int)(bbox.llx*points/1000),
366 (int)(gbbox.lly*points/1000),
367 (int)(bbox.urx*points/1000),
368 (int)(gbbox.ury*points/1000),
369 (int)(bbox.lly*points/1000),
370 (int)(bbox.ury*points/1000) ));
373 cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000;
374 cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000;
376 cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000;
377 cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000;
379 cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000;
380 cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000;
382 cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
383 cords[BBOX_RIGHT_BEARING] =
384 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
386 return BBOX_RIGHT_BEARING+1;
391 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
393 Interface to text rendering in a single color onto an image
395 im - pointer to image structure
396 xb - x coordinate of start of string
397 yb - y coordinate of start of string ( see align )
398 cl - color to draw the text in
399 fontnum - t1 library font id
400 points - number of points in fontheight
401 str - char pointer to string to render
403 align - (0 - top of font glyph | 1 - baseline )
409 i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,size_t len,int align, int utf8, char const *flags) {
412 int mod_flags = t1_get_flags(flags);
415 if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
419 char *work = t1_from_utf8(str, len, &worklen);
420 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
424 /* T1_AASetString() accepts a char * not a const char */
425 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
430 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
431 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
432 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
433 mm_log((1,"bpp: %d\n",glyph->bpp));
435 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
436 ysize=glyph->metrics.ascent-glyph->metrics.descent;
438 mm_log((1,"width: %d height: %d\n",xsize,ysize));
440 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
442 i_render_init(&r, im, xsize);
443 for(y=0;y<ysize;y++) {
444 i_render_color(&r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
452 =item t1_get_flags(flags)
454 Processes the characters in I<flags> to create a mod_flags value used
455 by some T1Lib functions.
460 t1_get_flags(char const *flags) {
461 int mod_flags = T1_KERNING;
465 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
466 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
467 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
468 /* ignore anything we don't recognize */
476 =item t1_from_utf8(char const *in, size_t len, int *outlen)
478 Produces an unencoded version of I<in> by dropping any Unicode
481 Returns a newly allocated buffer which should be freed with myfree().
482 Sets *outlen to the number of bytes used in the output string.
488 t1_from_utf8(char const *in, size_t len, int *outlen) {
489 /* at this point len is from a perl SV, so can't approach MAXINT */
490 char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
495 c = i_utf8_advance(&in, &len);
498 i_push_error(0, "invalid UTF8 character");
501 /* yeah, just drop them */
513 =item i_t1_has_chars(font_num, text, len, utf8, out)
515 Check if the given characters are defined by the font. Note that len
516 is the number of bytes, not the number of characters (when utf8 is
519 out[char index] will be true if the character exists.
521 Accepts UTF-8, but since T1 can only have 256 characters, any chars
522 with values over 255 will simply be returned as false.
524 Returns the number of characters that were checked.
530 i_t1_has_chars(int font_num, const char *text, size_t len, int utf8,
534 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n",
535 font_num, text, len, utf8));
538 if (T1_LoadFont(font_num)) {
546 c = i_utf8_advance(&text, &len);
548 i_push_error(0, "invalid UTF8 character");
553 c = (unsigned char)*text++;
558 /* limit of 256 characters for T1 */
562 char const * name = T1_GetCharName(font_num, (unsigned char)c);
565 *out++ = strcmp(name, ".notdef") != 0;
568 mm_log((2, " No name found for character %lx\n", c));
579 =item i_t1_face_name(font_num, name_buf, name_buf_size)
581 Copies the face name of the given C<font_num> to C<name_buf>. Returns
582 the number of characters required to store the name (which can be
583 larger than C<name_buf_size>, including the space required to store
584 the terminating NUL).
586 If name_buf is too small (as specified by name_buf_size) then the name
587 will be truncated. name_buf will always be NUL termintaed.
593 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
597 if (T1_LoadFont(font_num)) {
601 name = T1_GetFontName(font_num);
604 strncpy(name_buf, name, name_buf_size);
605 name_buf[name_buf_size-1] = '\0';
606 return strlen(name) + 1;
615 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
616 size_t name_buf_size) {
623 if (T1_LoadFont(font_num)) {
627 name = T1_GetCharName(font_num, (unsigned char)ch);
629 if (strcmp(name, ".notdef")) {
630 strncpy(name_buf, name, name_buf_size);
631 name_buf[name_buf_size-1] = '\0';
632 return strlen(name) + 1;
645 t1_push_error(void) {
648 i_push_error(0, "No error");
651 #ifdef T1ERR_SCAN_FONT_FORMAT
652 case T1ERR_SCAN_FONT_FORMAT:
653 i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
657 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
658 case T1ERR_SCAN_FILE_OPEN_ERR:
659 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
663 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
664 case T1ERR_SCAN_OUT_OF_MEMORY:
665 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
669 #ifdef T1ERR_SCAN_ERROR
670 case T1ERR_SCAN_ERROR:
671 i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
675 #ifdef T1ERR_SCAN_FILE_EOF
676 case T1ERR_SCAN_FILE_EOF:
677 i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
681 #ifdef T1ERR_PATH_ERROR
682 case T1ERR_PATH_ERROR:
683 i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
687 #ifdef T1ERR_PARSE_ERROR
688 case T1ERR_PARSE_ERROR:
689 i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
693 #ifdef T1ERR_TYPE1_ABORT
694 case T1ERR_TYPE1_ABORT:
695 i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
699 #ifdef T1ERR_INVALID_FONTID
700 case T1ERR_INVALID_FONTID:
701 i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
705 #ifdef T1ERR_INVALID_PARAMETER
706 case T1ERR_INVALID_PARAMETER:
707 i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
711 #ifdef T1ERR_OP_NOT_PERMITTED
712 case T1ERR_OP_NOT_PERMITTED:
713 i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
717 #ifdef T1ERR_ALLOC_MEM
718 case T1ERR_ALLOC_MEM:
719 i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
723 #ifdef T1ERR_FILE_OPEN_ERR
724 case T1ERR_FILE_OPEN_ERR:
725 i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
729 #ifdef T1ERR_UNSPECIFIED
730 case T1ERR_UNSPECIFIED:
731 i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
735 #ifdef T1ERR_NO_AFM_DATA
736 case T1ERR_NO_AFM_DATA:
737 i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
743 i_push_error(T1ERR_X11, "X11");
747 #ifdef T1ERR_COMPOSITE_CHAR
748 case T1ERR_COMPOSITE_CHAR:
749 i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
754 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
758 #endif /* HAVE_LIBT1 */
761 /* Truetype font support */
764 /* These are enabled by default when configuring Freetype 1.x
765 I haven't a clue how to reliably detect it at compile time.
767 We need a compilation probe in Makefile.PL
772 #include <freetype.h>
780 #include <ftxerr18.h>
783 /* some versions of FT1.x don't seem to define this - it's font defined
784 so it won't change */
785 #ifndef TT_MS_LANGID_ENGLISH_GENERAL
786 #define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
789 /* convert a code point into an index in the glyph cache */
790 #define TT_HASH(x) ((x) & 0xFF)
792 typedef struct i_glyph_entry_ {
797 #define TT_NOCHAR (~0UL)
799 struct TT_Instancehandle_ {
800 TT_Instance instance;
801 TT_Instance_Metrics imetrics;
802 TT_Glyph_Metrics gmetrics[256];
803 i_tt_glyph_entry glyphs[256];
809 typedef struct TT_Instancehandle_ TT_Instancehandle;
811 struct TT_Fonthandle_ {
813 TT_Face_Properties properties;
814 TT_Instancehandle instanceh[TT_CHC];
824 #define USTRCT(x) ((x).z)
825 #define TT_VALID( handle ) ( ( handle ).z != NULL )
827 static void i_tt_push_error(TT_Error rc);
831 static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
832 static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
833 static void i_tt_done_raster_map( TT_Raster_Map *bit );
834 static void i_tt_clear_raster_map( TT_Raster_Map* bit );
835 static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
836 static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
838 i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
839 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
840 int x_off, int y_off, int smooth );
842 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
843 TT_Raster_Map *small_bit, int cords[6],
844 char const* txt, size_t len, int smooth, int utf8 );
845 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
846 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
848 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
849 float points, char const* txt, size_t len, int smooth, int utf8 );
850 static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[6], int utf8 );
853 /* static globals needed */
855 static int TT_initialized = 0;
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 };
881 mm_log((1,"init_tt()\n"));
882 error = TT_Init_FreeType( &engine );
884 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
885 i_tt_push_error(error);
886 i_push_error(0, "Could not initialize freetype 1.x");
891 error = TT_Init_Post_Extension( engine );
893 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
895 i_tt_push_error(error);
896 i_push_error(0, "Could not initialize FT 1.x POST extension");
901 error = TT_Set_Raster_Gray_Palette(engine, palette);
903 mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
904 i_tt_push_error(error);
905 i_push_error(0, "Could not initialize FT 1.x POST extension");
916 =item i_tt_get_instance(handle, points, smooth)
918 Finds a points+smooth instance or if one doesn't exist in the cache
919 allocates room and returns its cache entry
921 fontname - path to the font to load
922 handle - handle to the font.
923 points - points of the requested font
924 smooth - boolean (True: antialias on, False: antialias is off)
931 i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
935 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
936 handle,points,smooth));
938 if (smooth == -1) { /* Smooth doesn't matter for this search */
939 for(i=0;i<TT_CHC;i++) {
940 if (handle->instanceh[i].ptsize==points) {
941 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
945 smooth=1; /* We will be adding a font - add it as smooth then */
946 } else { /* Smooth doesn't matter for this search */
947 for(i=0;i<TT_CHC;i++) {
948 if (handle->instanceh[i].ptsize == points
949 && handle->instanceh[i].smooth == smooth) {
950 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
956 /* Found the instance in the cache - return the cache index */
958 for(idx=0;idx<TT_CHC;idx++) {
959 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
962 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
963 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
964 USTRCT(handle->instanceh[idx].instance) ));
966 if ( USTRCT(handle->instanceh[idx].instance) ) {
967 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
969 /* Free cached glyphs */
971 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
972 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
975 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
976 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
979 /* Free instance if needed */
980 TT_Done_Instance( handle->instanceh[idx].instance );
983 /* create and initialize instance */
984 /* FIXME: probably a memory leak on fail */
986 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
987 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
988 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
991 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
995 /* Now that the instance should the inplace we need to lower all of the
996 ru counts and put `this' one with the highest entry */
998 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
1000 handle->instanceh[idx].order=TT_CHC-1;
1001 handle->instanceh[idx].ptsize=points;
1002 handle->instanceh[idx].smooth=smooth;
1003 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
1005 /* Zero the memory for the glyph storage so they are not thought as
1006 cached if they haven't been cached since this new font was loaded */
1008 for(i=0;i<256;i++) {
1009 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
1010 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
1018 =item i_tt_new(fontname)
1020 Creates a new font handle object, finds a character map and initialise the
1021 the font handle's cache
1023 fontname - path to the font to load
1029 i_tt_new(const char *fontname) {
1031 TT_Fonthandle *handle;
1033 unsigned short platform,encoding;
1035 if (!TT_initialized && i_init_tt()) {
1036 i_push_error(0, "Could not initialize FT1 engine");
1042 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
1044 /* allocate memory for the structure */
1046 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
1048 /* load the typeface */
1049 error = TT_Open_Face( engine, fontname, &handle->face );
1051 if ( error == TT_Err_Could_Not_Open_File ) {
1052 mm_log((1, "Could not find/open %s.\n", fontname ));
1055 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
1058 i_tt_push_error(error);
1062 TT_Get_Face_Properties( handle->face, &(handle->properties) );
1064 /* First, look for a Unicode charmap */
1065 n = handle->properties.num_CharMaps;
1066 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
1068 for ( i = 0; i < n; i++ ) {
1069 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
1070 if ( (platform == 3 && encoding == 1 )
1071 || (platform == 0 && encoding == 0 ) ) {
1072 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
1073 platform, encoding));
1074 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
1078 if (!USTRCT(handle->char_map) && n != 0) {
1079 /* just use the first one */
1080 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
1083 /* Zero the pointsizes - and ordering */
1085 for(i=0;i<TT_CHC;i++) {
1086 USTRCT(handle->instanceh[i].instance)=NULL;
1087 handle->instanceh[i].order=i;
1088 handle->instanceh[i].ptsize=0;
1089 handle->instanceh[i].smooth=-1;
1093 handle->loaded_names = 0;
1096 mm_log((1,"i_tt_new <- 0x%X\n",handle));
1103 * raster map management
1107 =item i_tt_init_raster_map(bit, width, height, smooth)
1109 Allocates internal memory for the bitmap as needed by the parameters (internal)
1111 bit - bitmap to allocate into
1112 width - width of the bitmap
1113 height - height of the bitmap
1114 smooth - boolean (True: antialias on, False: antialias is off)
1121 i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
1123 mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
1126 bit->width = ( width + 3 ) & -4;
1127 bit->flow = TT_Flow_Down;
1130 bit->cols = bit->width;
1131 bit->size = bit->rows * bit->width;
1133 bit->cols = ( bit->width + 7 ) / 8; /* convert to # of bytes */
1134 bit->size = bit->rows * bit->cols; /* number of bytes in buffer */
1137 /* rows can be 0 for some glyphs, for example ' ' */
1138 if (bit->rows && bit->size / bit->rows != bit->cols) {
1139 i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
1140 bit->width, bit->rows);
1143 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 ));
1145 bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
1146 if ( !bit->bitmap ) i_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
1151 =item i_tt_clear_raster_map(bit)
1153 Frees the bitmap data and sets pointer to NULL (internal)
1155 bit - bitmap to free
1162 i_tt_done_raster_map( TT_Raster_Map *bit ) {
1163 myfree( bit->bitmap );
1169 =item i_tt_clear_raster_map(bit)
1171 Clears the specified bitmap (internal)
1173 bit - bitmap to zero
1181 i_tt_clear_raster_map( TT_Raster_Map* bit ) {
1182 memset( bit->bitmap, 0, bit->size );
1187 =item i_tt_blit_or(dst, src, x_off, y_off)
1189 function that blits one raster map into another (internal)
1191 dst - destination bitmap
1193 x_off - x offset into the destination bitmap
1194 y_off - y offset into the destination bitmap
1201 i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
1204 unsigned char *s, *d;
1206 x1 = x_off < 0 ? -x_off : 0;
1207 y1 = y_off < 0 ? -y_off : 0;
1209 x2 = (int)dst->cols - x_off;
1210 if ( x2 > src->cols ) x2 = src->cols;
1212 y2 = (int)dst->rows - y_off;
1213 if ( y2 > src->rows ) y2 = src->rows;
1215 if ( x1 >= x2 ) return;
1217 /* do the real work now */
1219 for ( y = y1; y < y2; ++y ) {
1220 s = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
1221 d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
1223 for ( x = x1; x < x2; ++x ) {
1232 /* useful for debugging */
1235 static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
1237 fprintf(out, "cols %d rows %d flow %d\n", bit->cols, bit->rows, bit->flow);
1238 for (y = 0; y < bit->rows; ++y) {
1239 fprintf(out, "%2d:", y);
1240 for (x = 0; x < bit->cols; ++x) {
1241 if ((x & 7) == 0 && x) putc(' ', out);
1242 fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
1251 =item i_tt_get_glyph(handle, inst, j)
1253 Function to see if a glyph exists and if so cache it (internal)
1255 handle - pointer to font handle
1256 inst - font instance
1257 j - charcode of glyph
1264 i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j) {
1265 unsigned short load_flags, code;
1268 mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",
1269 handle,inst,j, ((j >= ' ' && j <= '~') ? j : '.')));
1271 /*mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));*/
1273 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)
1274 && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
1275 mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
1279 if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
1280 /* clean up the entry */
1281 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1282 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1283 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1286 /* Ok - it wasn't cached - try to get it in */
1287 load_flags = TTLOAD_SCALE_GLYPH;
1288 if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
1290 if ( !TT_VALID(handle->char_map) ) {
1291 code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
1292 if ( code >= handle->properties.num_Glyphs ) code = 0;
1293 } else code = TT_Char_Index( handle->char_map, j );
1295 if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[TT_HASH(j)].glyph)) ) {
1296 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1297 i_push_error(error, "TT_New_Glyph()");
1300 if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
1301 mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
1303 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1304 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1305 i_push_error(error, "TT_Load_Glyph()");
1309 /* At this point the glyph should be allocated and loaded */
1310 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
1312 /* Next get the glyph metrics */
1313 error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1314 &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
1316 mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
1317 TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
1318 USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
1319 handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
1320 i_push_error(error, "TT_Get_Glyph_Metrics()");
1328 =item i_tt_has_chars(handle, text, len, utf8, out)
1330 Check if the given characters are defined by the font. Note that len
1331 is the number of bytes, not the number of characters (when utf8 is
1334 Returns the number of characters that were checked.
1340 i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
1343 mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
1344 handle, text, len, utf8));
1350 c = i_utf8_advance(&text, &len);
1352 i_push_error(0, "invalid UTF8 character");
1357 c = (unsigned char)*text++;
1361 if (TT_VALID(handle->char_map)) {
1362 index = TT_Char_Index(handle->char_map, c);
1365 index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
1366 if (index >= handle->properties.num_Glyphs)
1369 *out++ = index != 0;
1377 =item i_tt_destroy(handle)
1379 Clears the data taken by a font including all cached data such as
1382 handle - pointer to font handle
1388 i_tt_destroy( TT_Fonthandle *handle) {
1389 TT_Close_Face( handle->face );
1392 /* FIXME: Should these be freed automatically by the library?
1394 TT_Done_Instance( instance );
1396 i_tt_done_glyphs( void ) {
1399 if ( !glyphs ) return;
1401 for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
1411 * FreeType Rendering functions
1416 =item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
1418 Renders a single glyph into the bit rastermap (internal)
1420 handle - pointer to font handle
1421 gmetrics - the metrics for the glyph to be rendered
1422 bit - large bitmap that is the destination for the text
1423 smallbit - small bitmap that is used only if smooth is true
1424 x_off - x offset of glyph
1425 y_off - y offset of glyph
1426 smooth - boolean (True: antialias on, False: antialias is off)
1433 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 ) {
1435 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",
1436 USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
1438 if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
1440 TT_F26Dot6 xmin, ymin, xmax, ymax;
1442 xmin = gmetrics->bbox.xMin & -64;
1443 ymin = gmetrics->bbox.yMin & -64;
1444 xmax = (gmetrics->bbox.xMax + 63) & -64;
1445 ymax = (gmetrics->bbox.yMax + 63) & -64;
1447 i_tt_clear_raster_map( small_bit );
1448 TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
1449 i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
1455 =item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
1457 calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
1459 handle - pointer to font handle
1460 inst - font instance
1461 bit - large bitmap that is the destination for the text
1462 smallbit - small bitmap that is used only if smooth is true
1463 txt - string to render
1464 len - length of the string to render
1465 smooth - boolean (True: antialias on, False: antialias is off)
1472 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
1473 TT_Raster_Map *small_bit, int cords[6],
1474 char const* txt, size_t len, int smooth, int utf8 ) {
1478 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",
1479 handle, inst, bit, small_bit, len, txt, len, smooth, utf8));
1482 y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
1485 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 */
1490 j = i_utf8_advance(&txt, &len);
1492 i_push_error(0, "invalid UTF8 character");
1497 j = (unsigned char)*txt++;
1500 if ( !i_tt_get_glyph(handle,inst,j) )
1502 i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph,
1503 &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit,
1504 small_bit, x, y, smooth );
1505 x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
1513 * Functions to render rasters (single channel images) onto images
1517 =item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
1519 Function to dump a raster onto an image in color used by i_tt_text() (internal).
1521 im - image to dump raster on
1522 bit - bitmap that contains the text to be dumped to im
1523 xb, yb - coordinates, left edge and baseline
1524 cl - color to use for text
1525 smooth - boolean (True: antialias on, False: antialias is off)
1532 i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ) {
1533 unsigned char *bmap;
1535 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));
1542 i_render_init(&r, im, bit->cols);
1543 for(y=0;y<bit->rows;y++) {
1545 for(x=0;x<bit->width;x++) {
1546 c = (unsigned char)bmap[y*(bit->cols)+x];
1548 i_gpix(im,x+xb,y+yb,&val);
1549 for(ch=0;ch<im->channels;ch++)
1550 val.channel[ch] = (c*cl->channel[ch]+i*val.channel[ch])/255;
1551 i_ppix(im,x+xb,y+yb,&val);
1554 i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
1559 for(y=0;y<bit->rows;y++) {
1560 unsigned mask = 0x80;
1561 unsigned char *p = bmap + y * bit->cols;
1563 for(x = 0; x < bit->width; x++) {
1565 i_ppix(im, x+xb, y+yb, cl);
1580 =item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
1582 Function to dump a raster onto a single channel image in color (internal)
1584 im - image to dump raster on
1585 bit - bitmap that contains the text to be dumped to im
1586 xb, yb - coordinates, left edge and baseline
1587 channel - channel to copy to
1588 smooth - boolean (True: antialias on, False: antialias is off)
1595 i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ) {
1596 unsigned char *bmap;
1599 int old_mask = im->ch_mask;
1600 im->ch_mask = 1 << channel;
1602 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));
1607 for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
1608 c = bmap[y*(bit->cols)+x];
1609 val.channel[channel] = c;
1610 i_ppix(im,x+xb,y+yb,&val);
1613 for(y=0;y<bit->rows;y++) {
1614 unsigned mask = 0x80;
1615 unsigned char *p = bmap + y * bit->cols;
1617 for(x=0;x<bit->width;x++) {
1618 val.channel[channel] = (*p & mask) ? 255 : 0;
1619 i_ppix(im,x+xb,y+yb,&val);
1629 im->ch_mask = old_mask;
1634 =item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth)
1636 interface for generating single channel raster of text (internal)
1638 handle - pointer to font handle
1639 bit - the bitmap that is allocated, rendered into and NOT freed
1640 cords - the bounding box (modified in place)
1641 points - font size to use
1642 txt - string to render
1643 len - length of the string to render
1644 smooth - boolean (True: antialias on, False: antialias is off)
1651 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, size_t len, int smooth, int utf8 ) {
1654 TT_Raster_Map small_bit;
1656 /* find or install an instance */
1657 if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) {
1658 mm_log((1,"i_tt_rasterize: get instance failed\n"));
1662 /* calculate bounding box */
1663 if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
1667 width = cords[2]-cords[0];
1668 height = cords[5]-cords[4];
1670 mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height ));
1672 i_tt_init_raster_map ( bit, width, height, smooth );
1673 i_tt_clear_raster_map( bit );
1674 if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
1676 if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len,
1679 i_tt_done_raster_map( &small_bit );
1683 if ( smooth ) i_tt_done_raster_map( &small_bit );
1690 * Exported text rendering interfaces
1695 =item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth, utf8)
1697 Interface to text rendering into a single channel in an image
1699 handle - pointer to font handle
1700 im - image to render text on to
1701 xb, yb - coordinates, left edge and baseline
1702 channel - channel to render into
1703 points - font size to use
1704 txt - string to render
1705 len - length of the string to render
1706 smooth - boolean (True: antialias on, False: antialias is off)
1712 i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, size_t len, int smooth, int utf8, int align ) {
1714 int cords[BOUNDING_BOX_COUNT];
1715 int ascent, st_offset, y;
1719 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1721 ascent=cords[BBOX_ASCENT];
1722 st_offset=cords[BBOX_NEG_WIDTH];
1723 y = align ? yb-ascent : yb;
1725 i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
1726 i_tt_done_raster_map( &bit );
1733 =item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8)
1735 Interface to text rendering in a single color onto an image
1737 handle - pointer to font handle
1738 im - image to render text on to
1739 xb, yb - coordinates, left edge and baseline
1740 cl - color to use for text
1741 points - font size to use
1742 txt - string to render
1743 len - length of the string to render
1744 smooth - boolean (True: antialias on, False: antialias is off)
1750 i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, size_t len, int smooth, int utf8, int align) {
1751 int cords[BOUNDING_BOX_COUNT];
1752 int ascent, st_offset, y;
1757 if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
1759 ascent=cords[BBOX_ASCENT];
1760 st_offset=cords[BBOX_NEG_WIDTH];
1761 y = align ? yb-ascent : yb;
1763 i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth );
1764 i_tt_done_raster_map( &bit );
1771 =item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8)
1773 Function to get texts bounding boxes given the instance of the font (internal)
1775 handle - pointer to font handle
1776 inst - font instance
1777 txt - string to measure
1778 len - length of the string to render
1779 cords - the bounding box (modified in place)
1786 i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
1787 int upm, casc, cdesc, first;
1798 unsigned char *ustr;
1799 ustr=(unsigned char*)txt;
1801 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));
1803 upm = handle->properties.header->Units_Per_EM;
1804 gascent = ( handle->properties.horizontal->Ascender * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
1805 gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
1810 mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
1815 j = i_utf8_advance(&txt, &len);
1817 i_push_error(0, "invalid UTF8 character");
1822 j = (unsigned char)*txt++;
1825 if ( i_tt_get_glyph(handle,inst,j) ) {
1826 TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
1827 width += gm->advance / 64;
1828 casc = (gm->bbox.yMax+63) / 64;
1829 cdesc = (gm->bbox.yMin-63) / 64;
1831 mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n",
1832 ((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
1835 start = gm->bbox.xMin / 64;
1836 ascent = (gm->bbox.yMax+63) / 64;
1837 descent = (gm->bbox.yMin-63) / 64;
1840 if (!len) { /* if at end of string */
1841 /* the right-side bearing - in case the right-side of a
1842 character goes past the right of the advance width,
1843 as is common for italic fonts
1845 rightb = gm->advance - gm->bearingX
1846 - (gm->bbox.xMax - gm->bbox.xMin);
1847 /* fprintf(stderr, "font info last: %d %d %d %d\n",
1848 gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
1851 ascent = (ascent > casc ? ascent : casc );
1852 descent = (descent < cdesc ? descent : cdesc);
1856 cords[BBOX_NEG_WIDTH]=start;
1857 cords[BBOX_GLOBAL_DESCENT]=gdescent;
1858 cords[BBOX_POS_WIDTH]=width;
1860 cords[BBOX_POS_WIDTH] -= rightb / 64;
1861 cords[BBOX_GLOBAL_ASCENT]=gascent;
1862 cords[BBOX_DESCENT]=descent;
1863 cords[BBOX_ASCENT]=ascent;
1864 cords[BBOX_ADVANCE_WIDTH] = width;
1865 cords[BBOX_RIGHT_BEARING] = rightb / 64;
1867 return BBOX_RIGHT_BEARING + 1;
1872 =item i_tt_bbox(handle, points, txt, len, cords, utf8)
1874 Interface to get a strings bounding box
1876 handle - pointer to font handle
1877 points - font size to use
1878 txt - string to render
1879 len - length of the string to render
1880 cords - the bounding box (modified in place)
1886 i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,size_t len,int cords[6], int utf8) {
1890 mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d, utf8 %d)\n",handle,points,len,txt,len, utf8));
1892 if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
1893 i_push_errorf(0, "i_tt_get_instance(%g)", points);
1894 mm_log((1,"i_tt_text: get instance failed\n"));
1898 return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
1902 =item i_tt_face_name(handle, name_buf, name_buf_size)
1904 Retrieve's the font's postscript name.
1906 This is complicated by the need to handle encodings and so on.
1911 i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
1912 TT_Face_Properties props;
1915 TT_UShort platform_id, encoding_id, lang_id, name_id;
1918 int want_index = -1; /* an acceptable but not perfect name */
1923 TT_Get_Face_Properties(handle->face, &props);
1924 name_count = props.num_Names;
1925 for (i = 0; i < name_count; ++i) {
1926 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1929 TT_Get_Name_String(handle->face, i, &name, &name_len);
1931 if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
1932 && name_id == TT_NAME_ID_PS_NAME) {
1933 int might_want_index = -1;
1934 int might_score = 0;
1935 if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
1937 (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
1938 /* exactly what we want */
1943 if (platform_id == TT_PLATFORM_MICROSOFT
1944 && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
1945 /* any english is good */
1946 might_want_index = i;
1949 /* there might be something in between */
1951 /* anything non-unicode is better than nothing */
1952 might_want_index = i;
1955 if (might_score > score) {
1956 score = might_score;
1957 want_index = might_want_index;
1962 if (want_index != -1) {
1963 TT_Get_Name_String(handle->face, want_index, &name, &name_len);
1965 strncpy(name_buf, name, name_buf_size);
1966 name_buf[name_buf_size-1] = '\0';
1968 return strlen(name) + 1;
1971 i_push_error(0, "no face name present");
1976 void i_tt_dump_names(TT_Fonthandle *handle) {
1977 TT_Face_Properties props;
1980 TT_UShort platform_id, encoding_id, lang_id, name_id;
1984 TT_Get_Face_Properties(handle->face, &props);
1985 name_count = props.num_Names;
1986 for (i = 0; i < name_count; ++i) {
1987 TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
1989 TT_Get_Name_String(handle->face, i, &name, &name_len);
1991 printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
1992 encoding_id, lang_id, name_id);
1993 if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
1994 printf("(unicode)\n");
1997 printf("'%s'\n", name);
2004 i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
2005 size_t name_buf_size) {
2013 if (!handle->loaded_names) {
2015 mm_log((1, "Loading PS Names"));
2016 handle->load_cond = TT_Load_PS_Names(handle->face, &post);
2017 ++handle->loaded_names;
2020 if (handle->load_cond) {
2021 i_push_errorf(handle->load_cond, "error loading names (%d)", handle->load_cond);
2025 index = TT_Char_Index(handle->char_map, ch);
2027 i_push_error(0, "no such character");
2031 rc = TT_Get_PS_Name(handle->face, index, &psname);
2034 i_push_error(rc, "error getting name");
2038 strncpy(name_buf, psname, name_buf_size);
2039 name_buf[name_buf_size-1] = '\0';
2041 return strlen(psname) + 1;
2043 mm_log((1, "FTXPOST extension not enabled\n"));
2045 i_push_error(0, "Use of FTXPOST extension disabled");
2052 =item i_tt_push_error(code)
2054 Push an error message and code onto the Imager error stack.
2059 i_tt_push_error(TT_Error rc) {
2061 TT_String const *msg = TT_ErrToString18(rc);
2063 i_push_error(rc, msg);
2065 i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
2069 #endif /* HAVE_LIBTT */
2077 Arnar M. Hrafnkelsson <addi@umich.edu>