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;
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];
219 T1_AASetBitsPerPixel( 8 );
220 T1_AASetLevel( T1_AA_NONE );
221 T1_AANSetGrayValues( 0, 255 );
222 mm_log((1,"setting T1 antialias to none\n"));
225 T1_AASetBitsPerPixel( 8 );
226 T1_AASetLevel( T1_AA_LOW );
227 T1_AASetGrayValues( 0,65,127,191,255 );
228 mm_log((1,"setting T1 antialias to low\n"));
231 T1_AASetBitsPerPixel(8);
232 T1_AASetLevel(T1_AA_HIGH);
233 for(i=0;i<17;i++) cst[i]=(i*255)/16;
234 T1_AAHSetGrayValues( cst );
235 mm_log((1,"setting T1 antialias to high\n"));
243 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align,aa)
245 Interface to text rendering into a single channel in an image
247 im pointer to image structure
248 xb x coordinate of start of string
249 yb y coordinate of start of string ( see align )
250 channel - destination channel
251 fontnum - t1 library font id
252 points - number of points in fontheight
253 str - string to render
255 align - (0 - top of font glyph | 1 - baseline )
256 aa - anti-aliasing level
262 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) {
266 int mod_flags = t1_get_flags(flags);
267 int fontnum = font->font_id;
269 unsigned int ch_mask_store;
273 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",
274 font, fontnum, im, i_DFcp(xb, yb), channel, points, str, (unsigned)len, align, utf8, flags, aa));
277 mm_log((1,"i_t1_cp: Null image in input\n"));
278 i_push_error(0, "null image");
288 char *work = t1_from_utf8(str, len, &worklen);
290 i_mutex_unlock(mutex);
293 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
297 glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
301 i_push_error(0, "i_t1_cp: T1_AASetString failed");
302 i_mutex_unlock(mutex);
306 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
307 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
308 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
309 mm_log((1,"bpp: %lu\n", (unsigned long)glyph->bpp));
311 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
312 ysize=glyph->metrics.ascent-glyph->metrics.descent;
314 mm_log((1,"width: %d height: %d\n",xsize,ysize));
316 ch_mask_store=im->ch_mask;
317 im->ch_mask=1<<channel;
319 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
321 for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
322 val.channel[channel]=glyph->bits[y*xsize+x];
323 i_ppix(im,x+xb,y+yb,&val);
326 im->ch_mask=ch_mask_store;
328 i_mutex_unlock(mutex);
334 t1_fix_bbox(BBox *bbox, const char *str, size_t len, int advance,
335 int space_position) {
336 /* never called with len == 0 */
337 if (str[0] == space_position && bbox->llx > 0)
339 if (str[len-1] == space_position && bbox->urx < advance)
341 if (bbox->lly > bbox->ury)
342 bbox->lly = bbox->ury = 0;
346 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
348 function to get a strings bounding box given the font id and sizes
350 handle - pointer to font handle
351 fontnum - t1 library font id
352 points - number of points in fontheight
353 str - string to measure
355 cords - the bounding box (modified in place)
361 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) {
364 int mod_flags = t1_get_flags(flags);
366 int fontnum = font->font_id;
373 space_position = T1_GetEncodingIndex(fontnum, "space");
375 mm_log((1,"i_t1_bbox(font %p (%d),points %.2f,str '%.*s', len %u)\n",
376 font, fontnum,points,(int)len,str,(unsigned)len));
377 if (T1_LoadFont(fontnum) == -1) {
379 i_mutex_unlock(mutex);
384 /* len == 0 has special meaning to T1lib, but it means there's
385 nothing to draw, so return that */
386 bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
392 char *work = t1_from_utf8(str, len, &worklen);
394 i_mutex_unlock(mutex);
397 advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
398 bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
399 t1_fix_bbox(&bbox, work, worklen, advance, space_position);
403 advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
404 bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
405 t1_fix_bbox(&bbox, str, len, advance, space_position);
408 gbbox = T1_GetFontBBox(fontnum);
410 mm_log((1,"bbox: (%d, %d, %d, %d, %d, %d)\n",
411 (int)(bbox.llx*points/1000),
412 (int)(gbbox.lly*points/1000),
413 (int)(bbox.urx*points/1000),
414 (int)(gbbox.ury*points/1000),
415 (int)(bbox.lly*points/1000),
416 (int)(bbox.ury*points/1000) ));
419 cords[BBOX_NEG_WIDTH]=((double)bbox.llx*points)/1000;
420 cords[BBOX_POS_WIDTH]=((double)bbox.urx*points)/1000;
422 cords[BBOX_GLOBAL_DESCENT]=((double)gbbox.lly*points)/1000;
423 cords[BBOX_GLOBAL_ASCENT]=((double)gbbox.ury*points)/1000;
425 cords[BBOX_DESCENT]=((double)bbox.lly*points)/1000;
426 cords[BBOX_ASCENT]=((double)bbox.ury*points)/1000;
428 cords[BBOX_ADVANCE_WIDTH] = ((double)advance * points)/1000;
429 cords[BBOX_RIGHT_BEARING] =
430 cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
432 i_mutex_unlock(mutex);
434 return BBOX_RIGHT_BEARING+1;
439 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align, utf8, flags, aa)
441 Interface to text rendering in a single color onto an image
443 im - pointer to image structure
444 xb - x coordinate of start of string
445 yb - y coordinate of start of string ( see align )
446 cl - color to draw the text in
447 fontnum - t1 library font id
448 points - number of points in fontheight
449 str - char pointer to string to render
451 align - (0 - top of font glyph | 1 - baseline )
453 flags - formatting flags
454 aa - anti-aliasing level
460 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) {
463 int mod_flags = t1_get_flags(flags);
465 int fontnum = font->font_id;
467 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",
468 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));
473 i_push_error(0, "null image");
474 mm_log((1,"i_t1_text: Null image in input\n"));
484 char *work = t1_from_utf8(str, len, &worklen);
486 i_mutex_unlock(mutex);
489 glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
493 /* T1_AASetString() accepts a char * not a const char */
494 glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
497 mm_log((1, "T1_AASetString failed\n"));
499 i_push_error(0, "i_t1_text(): T1_AASetString failed");
500 i_mutex_unlock(mutex);
504 mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
505 mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
506 mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
507 mm_log((1,"bpp: %lu\n",(unsigned long)glyph->bpp));
509 xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
510 ysize=glyph->metrics.ascent-glyph->metrics.descent;
512 mm_log((1,"width: %d height: %d\n",xsize,ysize));
514 if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
516 r = i_render_new(im, xsize);
517 for(y=0;y<ysize;y++) {
518 i_render_color(r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
522 i_mutex_unlock(mutex);
528 =item t1_get_flags(flags)
530 Processes the characters in I<flags> to create a mod_flags value used
531 by some T1Lib functions.
536 t1_get_flags(char const *flags) {
537 int mod_flags = T1_KERNING;
541 case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
542 case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
543 case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
544 /* ignore anything we don't recognize */
552 =item t1_from_utf8(char const *in, size_t len, int *outlen)
554 Produces an unencoded version of I<in> by dropping any Unicode
557 Returns a newly allocated buffer which should be freed with myfree().
558 Sets *outlen to the number of bytes used in the output string.
564 t1_from_utf8(char const *in, size_t len, int *outlen) {
565 /* at this point len is from a STRLEN which should be size_t and can't
566 be too big for mymalloc */
567 char *out = mymalloc(len+1); /* rechecked 29jul11 tonyc */
572 c = i_utf8_advance(&in, &len);
575 i_push_error(0, "invalid UTF8 character");
578 /* yeah, just drop them */
590 =item i_t1_has_chars(font_num, text, len, utf8, out)
592 Check if the given characters are defined by the font. Note that len
593 is the number of bytes, not the number of characters (when utf8 is
596 out[char index] will be true if the character exists.
598 Accepts UTF-8, but since T1 can only have 256 characters, any chars
599 with values over 255 will simply be returned as false.
601 Returns the number of characters that were checked.
607 i_t1_has_chars(i_t1_font_t font, const char *text, size_t len, int utf8,
610 int font_num = font->font_id;
614 mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %u, utf8 %d)\n",
615 font_num, text, (unsigned)len, utf8));
618 if (T1_LoadFont(font_num)) {
620 i_mutex_unlock(mutex);
627 c = i_utf8_advance(&text, &len);
629 i_push_error(0, "invalid UTF8 character");
630 i_mutex_unlock(mutex);
635 c = (unsigned char)*text++;
640 /* limit of 256 characters for T1 */
644 char const * name = T1_GetCharName(font_num, (unsigned char)c);
647 *out++ = strcmp(name, ".notdef") != 0;
650 mm_log((2, " No name found for character %lx\n", c));
657 i_mutex_unlock(mutex);
663 =item i_t1_face_name(font, name_buf, name_buf_size)
665 Copies the face name of the given C<font_num> to C<name_buf>. Returns
666 the number of characters required to store the name (which can be
667 larger than C<name_buf_size>, including the space required to store
668 the terminating NUL).
670 If name_buf is too small (as specified by name_buf_size) then the name
671 will be truncated. name_buf will always be NUL termintaed.
677 i_t1_face_name(i_t1_font_t font, char *name_buf, size_t name_buf_size) {
679 int font_num = font->font_id;
684 if (T1_LoadFont(font_num)) {
686 i_mutex_unlock(mutex);
689 name = T1_GetFontName(font_num);
692 size_t len = strlen(name);
693 strncpy(name_buf, name, name_buf_size);
694 name_buf[name_buf_size-1] = '\0';
695 i_mutex_unlock(mutex);
700 i_mutex_unlock(mutex);
706 i_t1_glyph_name(i_t1_font_t font, unsigned long ch, char *name_buf,
707 size_t name_buf_size) {
709 int font_num = font->font_id;
718 if (T1_LoadFont(font_num)) {
720 i_mutex_unlock(mutex);
723 name = T1_GetCharName(font_num, (unsigned char)ch);
725 if (strcmp(name, ".notdef")) {
726 size_t len = strlen(name);
727 strncpy(name_buf, name, name_buf_size);
728 name_buf[name_buf_size-1] = '\0';
729 i_mutex_unlock(mutex);
733 i_mutex_unlock(mutex);
739 i_mutex_unlock(mutex);
745 t1_push_error(void) {
746 #if T1LIB_VERSION > 5 || T1LIB_VERSION == 5 && T1LIB_VERSION >= 1
747 /* I don't know when T1_StrError() was introduced, be conservative */
748 i_push_error(T1_errno, T1_StrError(T1_errno));
752 i_push_error(0, "No error");
755 #ifdef T1ERR_SCAN_FONT_FORMAT
756 case T1ERR_SCAN_FONT_FORMAT:
757 i_push_error(T1ERR_SCAN_FONT_FORMAT, "Attempt to Load Multiple Master Font");
761 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
762 case T1ERR_SCAN_FILE_OPEN_ERR:
763 i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "Type 1 Font File Open Error");
767 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
768 case T1ERR_SCAN_OUT_OF_MEMORY:
769 i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "Virtual Memory Exceeded");
773 #ifdef T1ERR_SCAN_ERROR
774 case T1ERR_SCAN_ERROR:
775 i_push_error(T1ERR_SCAN_ERROR, "Syntactical Error Scanning Font File");
779 #ifdef T1ERR_SCAN_FILE_EOF
780 case T1ERR_SCAN_FILE_EOF:
781 i_push_error(T1ERR_SCAN_FILE_EOF, "Premature End of Font File Encountered");
785 #ifdef T1ERR_PATH_ERROR
786 case T1ERR_PATH_ERROR:
787 i_push_error(T1ERR_PATH_ERROR, "Path Construction Error");
791 #ifdef T1ERR_PARSE_ERROR
792 case T1ERR_PARSE_ERROR:
793 i_push_error(T1ERR_PARSE_ERROR, "Font is Corrupt");
797 #ifdef T1ERR_TYPE1_ABORT
798 case T1ERR_TYPE1_ABORT:
799 i_push_error(T1ERR_TYPE1_ABORT, "Rasterization Aborted");
803 #ifdef T1ERR_INVALID_FONTID
804 case T1ERR_INVALID_FONTID:
805 i_push_error(T1ERR_INVALID_FONTID, "Font ID Invalid in this Context");
809 #ifdef T1ERR_INVALID_PARAMETER
810 case T1ERR_INVALID_PARAMETER:
811 i_push_error(T1ERR_INVALID_PARAMETER, "Invalid Argument in Function Call");
815 #ifdef T1ERR_OP_NOT_PERMITTED
816 case T1ERR_OP_NOT_PERMITTED:
817 i_push_error(T1ERR_OP_NOT_PERMITTED, "Operation not Permitted");
821 #ifdef T1ERR_ALLOC_MEM
822 case T1ERR_ALLOC_MEM:
823 i_push_error(T1ERR_ALLOC_MEM, "Memory Allocation Error");
827 #ifdef T1ERR_FILE_OPEN_ERR
828 case T1ERR_FILE_OPEN_ERR:
829 i_push_error(T1ERR_FILE_OPEN_ERR, "Error Opening File");
833 #ifdef T1ERR_UNSPECIFIED
834 case T1ERR_UNSPECIFIED:
835 i_push_error(T1ERR_UNSPECIFIED, "Unspecified T1Lib Error");
839 #ifdef T1ERR_NO_AFM_DATA
840 case T1ERR_NO_AFM_DATA:
841 i_push_error(T1ERR_NO_AFM_DATA, "Missing AFM Data");
847 i_push_error(T1ERR_X11, "X11 Interface Error");
851 #ifdef T1ERR_COMPOSITE_CHAR
852 case T1ERR_COMPOSITE_CHAR:
853 i_push_error(T1ERR_COMPOSITE_CHAR, "Missing Component of Composite Character");
857 #ifdef T1ERR_SCAN_ENCODING
858 case T1ERR_SCAN_ENCODING:
859 i_push_error(T1ERR_SCAN_ENCODING, "Error Scanning Encoding File");
864 i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);