6 static int t1_get_flags(char const *flags);
7 static char *t1_from_utf8(char const *in, size_t len, int *outlen);
8 static undef_int i_init_t1_low(int t1log);
9 static void t1_push_error(void);
10 static void i_t1_set_aa(int st);
12 static int t1_active_fonts = 0;
13 static int t1_initialized = 0;
14 static int t1_aa = -1;
16 struct i_t1_font_tag {
20 static i_mutex_t mutex;
25 Initialize the font driver. This does not actually initialize T1Lib,
26 it just allocates the mutex we use to gate access to it.
33 mutex = i_mutex_new();
37 =item i_init_t1(t1log)
39 Initializes the t1lib font rendering engine.
45 i_init_t1(int t1log) {
49 result = i_init_t1_low(t1log);
51 i_mutex_unlock(mutex);
57 i_init_t1_low(int t1log) {
58 int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
60 mm_log((1,"init_t1(%d)\n", t1log));
64 if (t1_active_fonts) {
65 mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
66 i_push_error(0, "Cannot re-initialize T1 - active fonts");
76 init_flags |= LOGFILE;
77 if ((T1_InitLib(init_flags) == NULL)){
78 mm_log((1,"Initialization of t1lib failed\n"));
79 i_push_error(0, "T1_InitLib failed");
82 T1_SetLogLevel(T1LOG_DEBUG);
92 Shuts the t1lib font rendering engine down.
94 This it seems that this function is never used.
104 i_mutex_unlock(mutex);
109 =item i_t1_new(pfb, afm)
111 Loads the fonts with the given filenames, returns its font id
113 pfb - path to pfb file for font
114 afm - path to afm file for font
120 i_t1_new(char *pfb,char *afm) {
128 if (!t1_initialized && i_init_t1_low(0)) {
129 i_mutex_unlock(mutex);
133 mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
134 font_id = T1_AddFont(pfb);
136 mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
138 i_mutex_unlock(mutex);
143 mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
144 if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
147 if (T1_LoadFont(font_id)) {
148 mm_log((1, "i_t1_new() -> -1 - T1_LoadFont failed (%d)\n", T1_errno));
150 i_push_error(0, "loading font");
151 T1_DeleteFont(font_id);
152 i_mutex_unlock(mutex);
158 i_mutex_unlock(mutex);
160 font = mymalloc(sizeof(*font));
161 font->font_id = font_id;
163 mm_log((1, "i_t1_new() -> %p (%d)\n", font, font_id));
169 =item i_t1_destroy(font)
171 Frees resources for a t1 font with given font id.
179 i_t1_destroy(i_t1_font_t font) {
184 mm_log((1,"i_t1_destroy(font %p (%d))\n", font, font->font_id));
188 result = T1_DeleteFont(font->font_id);
191 i_mutex_unlock(mutex);
198 =item i_t1_set_aa(st)
200 Sets the antialiasing level of the t1 library.
202 st - 0 = NONE, 1 = LOW, 2 = HIGH.
204 Must be called with the mutex locked.
210 i_t1_set_aa(int st) {
212 unsigned long cst[17];
214 mm_log((1, "i_t1_set_aa(%d)\n", st));
221 T1_AASetBitsPerPixel( 8 );
222 T1_AASetLevel( T1_AA_NONE );
223 T1_AANSetGrayValues( 0, 255 );
224 mm_log((1,"setting T1 antialias to none\n"));
227 T1_AASetBitsPerPixel( 8 );
228 T1_AASetLevel( T1_AA_LOW );
229 T1_AASetGrayValues( 0,65,127,191,255 );
230 mm_log((1,"setting T1 antialias to low\n"));
233 T1_AASetBitsPerPixel(8);
234 T1_AASetLevel(T1_AA_HIGH);
235 for(i=0;i<17;i++) cst[i]=(i*255)/16;
236 T1_AAHSetGrayValues( cst );
237 mm_log((1,"setting T1 antialias to high\n"));
245 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align,aa)
247 Interface to text rendering into a single channel in an image
249 im pointer to image structure
250 xb x coordinate of start of string
251 yb y coordinate of start of string ( see align )
252 channel - destination channel
253 fontnum - t1 library font id
254 points - number of points in fontheight
255 str - string to render
257 align - (0 - top of font glyph | 1 - baseline )
258 aa - anti-aliasing level
264 i_t1_cp(i_t1_font_t font, i_img *im,i_img_dim xb,i_img_dim yb,int channel,double points,char* str,size_t len,int align, int utf8, char const *flags, int aa) {
268 int mod_flags = t1_get_flags(flags);
269 int fontnum = font->font_id;
271 unsigned int ch_mask_store;
275 mm_log((1, "i_t1_cp(font %p (%d), im %p, (xb,yb)=" i_DFp ", channel %d, points %g, str %p, len %u, align %d, utf8 %d, flags '%s', aa %d)\n",
276 font, fontnum, im, i_DFcp(xb, yb), channel, points, str, (unsigned)len, align, utf8, flags, aa));
279 mm_log((1,"i_t1_cp: Null image in input\n"));
280 i_push_error(0, "null image");
290 char *work = t1_from_utf8(str, len, &worklen);
292 i_mutex_unlock(mutex);
295 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
299 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
303 i_push_error(0, "i_t1_cp: T1_AASetString failed");
304 i_mutex_unlock(mutex);
308 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
309 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
310 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
311 mm_log((1,"bpp: %lu\n", (unsigned long)glyph->bpp));
313 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
314 ysize=glyph->metrics.ascent-glyph->metrics.descent;
316 mm_log((1,"width: %d height: %d\n",xsize,ysize));
318 ch_mask_store=im->ch_mask;
319 im->ch_mask=1<<channel;
321 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
323 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
324 val.channel[channel]=glyph->bits[y*xsize+x];
325 i_ppix(im,x+xb,y+yb,&val);
328 im->ch_mask=ch_mask_store;
330 i_mutex_unlock(mutex);
336 t1_fix_bbox(BBox *bbox, const char *str, size_t len, int advance,
337 int space_position) {
338 /* never called with len == 0 */
339 if (str[0] == space_position && bbox->llx > 0)
341 if (str[len-1] == space_position && bbox->urx < advance)
343 if (bbox->lly > bbox->ury)
344 bbox->lly = bbox->ury = 0;
348 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
350 function to get a strings bounding box given the font id and sizes
352 handle - pointer to font handle
353 fontnum - t1 library font id
354 points - number of points in fontheight
355 str - string to measure
357 cords - the bounding box (modified in place)
363 i_t1_bbox(i_t1_font_t font, double points,const char *str,size_t len, i_img_dim cords[6], int utf8,char const *flags) {
366 int mod_flags = t1_get_flags(flags);
368 int fontnum = font->font_id;
375 space_position = T1_GetEncodingIndex(fontnum, "space");
377 mm_log((1,"i_t1_bbox(font %p (%d),points %.2f,str '%.*s', len %u)\n",
378 font, fontnum,points,(int)len,str,(unsigned)len));
379 if (T1_LoadFont(fontnum) == -1) {
381 i_mutex_unlock(mutex);
386 /* len == 0 has special meaning to T1lib, but it means there's
387 nothing to draw, so return that */
388 bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
394 char *work = t1_from_utf8(str, len, &worklen);
396 i_mutex_unlock(mutex);
399 advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
400 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
401 t1_fix_bbox(&bbox, work, worklen, advance, space_position);
405 advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
406 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
407 t1_fix_bbox(&bbox, str, len, advance, space_position);
410 gbbox = T1_GetFontBBox(fontnum);
412 mm_log((1,"bbox: (%d, %d, %d, %d, %d, %d)\n",
413 (int)(bbox.llx*points/1000),
414 (int)(gbbox.lly*points/1000),
415 (int)(bbox.urx*points/1000),
416 (int)(gbbox.ury*points/1000),
417 (int)(bbox.lly*points/1000),
418 (int)(bbox.ury*points/1000) ));
421 cords[BBOX_NEG_WIDTH]=((double)bbox.llx*points)/1000;
422 cords[BBOX_POS_WIDTH]=((double)bbox.urx*points)/1000;
424 cords[BBOX_GLOBAL_DESCENT]=((double)gbbox.lly*points)/1000;
425 cords[BBOX_GLOBAL_ASCENT]=((double)gbbox.ury*points)/1000;
427 cords[BBOX_DESCENT]=((double)bbox.lly*points)/1000;
428 cords[BBOX_ASCENT]=((double)bbox.ury*points)/1000;
430 cords[BBOX_ADVANCE_WIDTH] = ((double)advance * points)/1000;
431 cords[BBOX_RIGHT_BEARING] =
432 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
434 i_mutex_unlock(mutex);
436 return BBOX_RIGHT_BEARING+1;
441 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align, utf8, flags, aa)
443 Interface to text rendering in a single color onto an image
445 im - pointer to image structure
446 xb - x coordinate of start of string
447 yb - y coordinate of start of string ( see align )
448 cl - color to draw the text in
449 fontnum - t1 library font id
450 points - number of points in fontheight
451 str - char pointer to string to render
453 align - (0 - top of font glyph | 1 - baseline )
455 flags - formatting flags
456 aa - anti-aliasing level
462 i_t1_text(i_t1_font_t font, i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl, double points,const char* str,size_t len,int align, int utf8, char const *flags, int aa) {
465 int mod_flags = t1_get_flags(flags);
467 int fontnum = font->font_id;
469 mm_log((1, "i_t1_text(font %p (%d), im %p, (xb,yb)=" i_DFp ", cl (%d,%d,%d,%d), points %g, str %p, len %u, align %d, utf8 %d, flags '%s', aa %d)\n",
470 font, fontnum, im, i_DFcp(xb, yb), cl->rgba.r, cl->rgba.g, cl->rgba.b, cl->rgba.a, points, str, (unsigned)len, align, utf8, flags, aa));
475 i_push_error(0, "null image");
476 mm_log((1,"i_t1_text: Null image in input\n"));
486 char *work = t1_from_utf8(str, len, &worklen);
488 i_mutex_unlock(mutex);
491 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
495 /* T1_AASetString() accepts a char * not a const char */
496 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
499 mm_log((1, "T1_AASetString failed\n"));
501 i_push_error(0, "i_t1_text(): T1_AASetString failed");
502 i_mutex_unlock(mutex);
506 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
507 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
508 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
509 mm_log((1,"bpp: %lu\n",(unsigned long)glyph->bpp));
511 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
512 ysize=glyph->metrics.ascent-glyph->metrics.descent;
514 mm_log((1,"width: %d height: %d\n",xsize,ysize));
516 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
518 r = i_render_new(im, xsize);
519 for(y=0;y<ysize;y++) {
520 i_render_color(r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
524 i_mutex_unlock(mutex);
530 =item t1_get_flags(flags)
532 Processes the characters in I<flags> to create a mod_flags value used
533 by some T1Lib functions.
538 t1_get_flags(char const *flags) {
539 int mod_flags = T1_KERNING;
543 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
544 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
545 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
546 /* ignore anything we don't recognize */
554 =item t1_from_utf8(char const *in, size_t len, int *outlen)
556 Produces an unencoded version of I<in> by dropping any Unicode
559 Returns a newly allocated buffer which should be freed with myfree().
560 Sets *outlen to the number of bytes used in the output string.
566 t1_from_utf8(char const *in, size_t len, int *outlen) {
567 /* at this point len is from a STRLEN which should be size_t and can't
568 be too big for mymalloc */
569 char *out = mymalloc(len+1); /* rechecked 29jul11 tonyc */
574 c = i_utf8_advance(&in, &len);
577 i_push_error(0, "invalid UTF8 character");
580 /* yeah, just drop them */
592 =item i_t1_has_chars(font_num, text, len, utf8, out)
594 Check if the given characters are defined by the font. Note that len
595 is the number of bytes, not the number of characters (when utf8 is
598 out[char index] will be true if the character exists.
600 Accepts UTF-8, but since T1 can only have 256 characters, any chars
601 with values over 255 will simply be returned as false.
603 Returns the number of characters that were checked.
609 i_t1_has_chars(i_t1_font_t font, const char *text, size_t len, int utf8,
612 int font_num = font->font_id;
616 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %u, utf8 %d)\n",
617 font_num, text, (unsigned)len, utf8));
620 if (T1_LoadFont(font_num)) {
622 i_mutex_unlock(mutex);
629 c = i_utf8_advance(&text, &len);
631 i_push_error(0, "invalid UTF8 character");
632 i_mutex_unlock(mutex);
637 c = (unsigned char)*text++;
642 /* limit of 256 characters for T1 */
646 char const * name = T1_GetCharName(font_num, (unsigned char)c);
649 *out++ = strcmp(name, ".notdef") != 0;
652 mm_log((2, " No name found for character %lx\n", c));
659 i_mutex_unlock(mutex);
665 =item i_t1_face_name(font, name_buf, name_buf_size)
667 Copies the face name of the given C<font_num> to C<name_buf>. Returns
668 the number of characters required to store the name (which can be
669 larger than C<name_buf_size>, including the space required to store
670 the terminating NUL).
672 If name_buf is too small (as specified by name_buf_size) then the name
673 will be truncated. name_buf will always be NUL termintaed.
679 i_t1_face_name(i_t1_font_t font, char *name_buf, size_t name_buf_size) {
681 int font_num = font->font_id;
686 if (T1_LoadFont(font_num)) {
688 i_mutex_unlock(mutex);
691 name = T1_GetFontName(font_num);
694 size_t len = strlen(name);
695 strncpy(name_buf, name, name_buf_size);
696 name_buf[name_buf_size-1] = '\0';
697 i_mutex_unlock(mutex);
702 i_mutex_unlock(mutex);
708 i_t1_glyph_name(i_t1_font_t font, unsigned long ch, char *name_buf,
709 size_t name_buf_size) {
711 int font_num = font->font_id;
720 if (T1_LoadFont(font_num)) {
722 i_mutex_unlock(mutex);
725 name = T1_GetCharName(font_num, (unsigned char)ch);
727 if (strcmp(name, ".notdef")) {
728 size_t len = strlen(name);
729 strncpy(name_buf, name, name_buf_size);
730 name_buf[name_buf_size-1] = '\0';
731 i_mutex_unlock(mutex);
735 i_mutex_unlock(mutex);
741 i_mutex_unlock(mutex);
747 t1_push_error(void) {
748 #if T1LIB_VERSION > 5 || T1LIB_VERSION == 5 && T1LIB_VERSION >= 1
749 /* I don't know when T1_StrError() was introduced, be conservative */
750 i_push_error(T1_errno, T1_StrError(T1_errno));
754 i_push_error(0, "No error");
757 #ifdef T1ERR_SCAN_FONT_FORMAT
758 case T1ERR_SCAN_FONT_FORMAT:
759 i_push_error(T1ERR_SCAN_FONT_FORMAT, "Attempt to Load Multiple Master Font");
763 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
764 case T1ERR_SCAN_FILE_OPEN_ERR:
765 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "Type 1 Font File Open Error");
769 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
770 case T1ERR_SCAN_OUT_OF_MEMORY:
771 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "Virtual Memory Exceeded");
775 #ifdef T1ERR_SCAN_ERROR
776 case T1ERR_SCAN_ERROR:
777 i_push_error(T1ERR_SCAN_ERROR, "Syntactical Error Scanning Font File");
781 #ifdef T1ERR_SCAN_FILE_EOF
782 case T1ERR_SCAN_FILE_EOF:
783 i_push_error(T1ERR_SCAN_FILE_EOF, "Premature End of Font File Encountered");
787 #ifdef T1ERR_PATH_ERROR
788 case T1ERR_PATH_ERROR:
789 i_push_error(T1ERR_PATH_ERROR, "Path Construction Error");
793 #ifdef T1ERR_PARSE_ERROR
794 case T1ERR_PARSE_ERROR:
795 i_push_error(T1ERR_PARSE_ERROR, "Font is Corrupt");
799 #ifdef T1ERR_TYPE1_ABORT
800 case T1ERR_TYPE1_ABORT:
801 i_push_error(T1ERR_TYPE1_ABORT, "Rasterization Aborted");
805 #ifdef T1ERR_INVALID_FONTID
806 case T1ERR_INVALID_FONTID:
807 i_push_error(T1ERR_INVALID_FONTID, "Font ID Invalid in this Context");
811 #ifdef T1ERR_INVALID_PARAMETER
812 case T1ERR_INVALID_PARAMETER:
813 i_push_error(T1ERR_INVALID_PARAMETER, "Invalid Argument in Function Call");
817 #ifdef T1ERR_OP_NOT_PERMITTED
818 case T1ERR_OP_NOT_PERMITTED:
819 i_push_error(T1ERR_OP_NOT_PERMITTED, "Operation not Permitted");
823 #ifdef T1ERR_ALLOC_MEM
824 case T1ERR_ALLOC_MEM:
825 i_push_error(T1ERR_ALLOC_MEM, "Memory Allocation Error");
829 #ifdef T1ERR_FILE_OPEN_ERR
830 case T1ERR_FILE_OPEN_ERR:
831 i_push_error(T1ERR_FILE_OPEN_ERR, "Error Opening File");
835 #ifdef T1ERR_UNSPECIFIED
836 case T1ERR_UNSPECIFIED:
837 i_push_error(T1ERR_UNSPECIFIED, "Unspecified T1Lib Error");
841 #ifdef T1ERR_NO_AFM_DATA
842 case T1ERR_NO_AFM_DATA:
843 i_push_error(T1ERR_NO_AFM_DATA, "Missing AFM Data");
849 i_push_error(T1ERR_X11, "X11 Interface Error");
853 #ifdef T1ERR_COMPOSITE_CHAR
854 case T1ERR_COMPOSITE_CHAR:
855 i_push_error(T1ERR_COMPOSITE_CHAR, "Missing Component of Composite Character");
859 #ifdef T1ERR_SCAN_ENCODING
860 case T1ERR_SCAN_ENCODING:
861 i_push_error(T1ERR_SCAN_ENCODING, "Error Scanning Encoding File");
866 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);