]> git.imager.perl.org - imager.git/blob - T1/imt1.c
Merge branch 'png'
[imager.git] / T1 / imt1.c
1 #include "imext.h"
2 #include "imt1.h"
3 #include <t1lib.h>
4 #include <string.h>
5
6 static int t1_get_flags(char const *flags);
7 static char *t1_from_utf8(char const *in, size_t len, int *outlen);
8
9 static void t1_push_error(void);
10
11 static int t1_active_fonts = 0;
12 static int t1_initialized = 0;
13
14 /* 
15 =item i_init_t1(t1log)
16
17 Initializes the t1lib font rendering engine.
18
19 =cut
20 */
21
22 undef_int
23 i_init_t1(int t1log) {
24   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
25   mm_log((1,"init_t1()\n"));
26
27   i_clear_error();
28
29   if (t1_active_fonts) {
30     mm_log((1, "Cannot re-initialize T1 - active fonts\n"));
31     i_push_error(0, "Cannot re-initialize T1 - active fonts");
32     return 1;
33   }
34
35   if (t1_initialized) {
36     T1_CloseLib();
37     t1_initialized = 0;
38   }
39   
40   if (t1log)
41     init_flags |= LOGFILE;
42   if ((T1_InitLib(init_flags) == NULL)){
43     mm_log((1,"Initialization of t1lib failed\n"));
44     i_push_error(0, "T1_InitLib failed");
45     return(1);
46   }
47   T1_SetLogLevel(T1LOG_DEBUG);
48   i_t1_set_aa(1); /* Default Antialias value */
49
50   ++t1_initialized;
51
52   return(0);
53 }
54
55 /* 
56 =item i_close_t1()
57
58 Shuts the t1lib font rendering engine down.
59
60   This it seems that this function is never used.
61
62 =cut
63 */
64
65 void
66 i_close_t1(void) {
67   T1_CloseLib();
68   t1_initialized = 0;
69 }
70
71
72 /*
73 =item i_t1_new(pfb, afm)
74
75 Loads the fonts with the given filenames, returns its font id
76
77  pfb -  path to pfb file for font
78  afm -  path to afm file for font
79
80 =cut
81 */
82
83 int
84 i_t1_new(char *pfb,char *afm) {
85   int font_id;
86
87   i_clear_error();
88
89   if (!t1_initialized && i_init_t1(0))
90     return -1;
91
92   mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
93   font_id = T1_AddFont(pfb);
94   if (font_id<0) {
95     mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
96     t1_push_error();
97     return font_id;
98   }
99   
100   if (afm != NULL) {
101     mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
102     if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
103   }
104
105   if (T1_LoadFont(font_id)) {
106     mm_log((1, "i_t1_new() -> -1 - T1_LoadFont failed (%d)\n", T1_errno));
107     t1_push_error();
108     i_push_error(0, "loading font");
109     T1_DeleteFont(font_id);
110     return -1;
111   }
112
113   ++t1_active_fonts;
114
115   mm_log((1, "i_t1_new() -> %d\n", font_id));
116
117   return font_id;
118 }
119
120 /*
121 =item i_t1_destroy(font_id)
122
123 Frees resources for a t1 font with given font id.
124
125    font_id - number of the font to free
126
127 =cut
128 */
129
130 int
131 i_t1_destroy(int font_id) {
132   mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
133
134   --t1_active_fonts;
135
136   return T1_DeleteFont(font_id);
137 }
138
139
140 /*
141 =item i_t1_set_aa(st)
142
143 Sets the antialiasing level of the t1 library.
144
145    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
146
147 =cut
148 */
149
150 void
151 i_t1_set_aa(int st) {
152   int i;
153   unsigned long cst[17];
154   switch(st) {
155   case 0:
156     T1_AASetBitsPerPixel( 8 );
157     T1_AASetLevel( T1_AA_NONE );
158     T1_AANSetGrayValues( 0, 255 );
159     mm_log((1,"setting T1 antialias to none\n"));
160     break;
161   case 1:
162     T1_AASetBitsPerPixel( 8 );
163     T1_AASetLevel( T1_AA_LOW );
164     T1_AASetGrayValues( 0,65,127,191,255 );
165     mm_log((1,"setting T1 antialias to low\n"));
166     break;
167   case 2:
168     T1_AASetBitsPerPixel(8);
169     T1_AASetLevel(T1_AA_HIGH);
170     for(i=0;i<17;i++) cst[i]=(i*255)/16;
171     T1_AAHSetGrayValues( cst );
172     mm_log((1,"setting T1 antialias to high\n"));
173   }
174 }
175
176
177 /* 
178 =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
179
180 Interface to text rendering into a single channel in an image
181
182    im        pointer to image structure
183    xb        x coordinate of start of string
184    yb        y coordinate of start of string ( see align )
185    channel - destination channel
186    fontnum - t1 library font id
187    points  - number of points in fontheight
188    str     - string to render
189    len     - string length
190    align   - (0 - top of font glyph | 1 - baseline )
191
192 =cut
193 */
194
195 undef_int
196 i_t1_cp(i_img *im,i_img_dim xb,i_img_dim yb,int channel,int fontnum,double points,char* str,size_t len,int align, int utf8, char const *flags) {
197   GLYPH *glyph;
198   int xsize,ysize,x,y;
199   i_color val;
200   int mod_flags = t1_get_flags(flags);
201
202   unsigned int ch_mask_store;
203   
204   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
205
206   if (utf8) {
207     int worklen;
208     char *work = t1_from_utf8(str, len, &worklen);
209     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
210     myfree(work);
211   }
212   else {
213     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
214   }
215   if (glyph == NULL)
216     return 0;
217
218   mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
219   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
220   mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
221   mm_log((1,"bpp: %d\n",glyph->bpp));
222   
223   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
224   ysize=glyph->metrics.ascent-glyph->metrics.descent;
225   
226   mm_log((1,"width: %d height: %d\n",xsize,ysize));
227
228   ch_mask_store=im->ch_mask;
229   im->ch_mask=1<<channel;
230
231   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
232   
233   for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
234     val.channel[channel]=glyph->bits[y*xsize+x];
235     i_ppix(im,x+xb,y+yb,&val);
236   }
237   
238   im->ch_mask=ch_mask_store;
239   return 1;
240 }
241
242 static void
243 t1_fix_bbox(BBox *bbox, const char *str, size_t len, int advance, 
244             int space_position) {
245   /* never called with len == 0 */
246   if (str[0] == space_position && bbox->llx > 0)
247     bbox->llx = 0;
248   if (str[len-1] == space_position && bbox->urx < advance)
249     bbox->urx = advance;
250   if (bbox->lly > bbox->ury)
251     bbox->lly = bbox->ury = 0; 
252 }
253
254 /*
255 =item i_t1_bbox(handle, fontnum, points, str, len, cords)
256
257 function to get a strings bounding box given the font id and sizes
258
259    handle  - pointer to font handle   
260    fontnum - t1 library font id
261    points  - number of points in fontheight
262    str     - string to measure
263    len     - string length
264    cords   - the bounding box (modified in place)
265
266 =cut
267 */
268
269 int
270 i_t1_bbox(int fontnum, double points,const char *str,size_t len, i_img_dim cords[6], int utf8,char const *flags) {
271   BBox bbox;
272   BBox gbbox;
273   int mod_flags = t1_get_flags(flags);
274   i_img_dim advance;
275   int space_position = T1_GetEncodingIndex(fontnum, "space");
276   
277   mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
278   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
279
280   if (len == 0) {
281     /* len == 0 has special meaning to T1lib, but it means there's
282        nothing to draw, so return that */
283     bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0;
284     advance = 0;
285   }
286   else {
287     if (utf8) {
288       int worklen;
289       char *work = t1_from_utf8(str, len, &worklen);
290       advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags);
291       bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags);
292       t1_fix_bbox(&bbox, work, worklen, advance, space_position);
293       myfree(work);
294     }
295     else {
296       advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags);
297       bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags);
298       t1_fix_bbox(&bbox, str, len, advance, space_position);
299     }
300   }
301   gbbox = T1_GetFontBBox(fontnum);
302   
303   mm_log((1,"bbox: (%d,%d,%d,%d)\n",
304           (int)(bbox.llx*points/1000),
305           (int)(gbbox.lly*points/1000),
306           (int)(bbox.urx*points/1000),
307           (int)(gbbox.ury*points/1000),
308           (int)(bbox.lly*points/1000),
309           (int)(bbox.ury*points/1000) ));
310
311
312   cords[BBOX_NEG_WIDTH]=((double)bbox.llx*points)/1000;
313   cords[BBOX_POS_WIDTH]=((double)bbox.urx*points)/1000;
314
315   cords[BBOX_GLOBAL_DESCENT]=((double)gbbox.lly*points)/1000;
316   cords[BBOX_GLOBAL_ASCENT]=((double)gbbox.ury*points)/1000;
317
318   cords[BBOX_DESCENT]=((double)bbox.lly*points)/1000;
319   cords[BBOX_ASCENT]=((double)bbox.ury*points)/1000;
320
321   cords[BBOX_ADVANCE_WIDTH] = ((double)advance * points)/1000;
322   cords[BBOX_RIGHT_BEARING] = 
323     cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
324
325   return BBOX_RIGHT_BEARING+1;
326 }
327
328
329 /*
330 =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
331
332 Interface to text rendering in a single color onto an image
333
334    im      - pointer to image structure
335    xb      - x coordinate of start of string
336    yb      - y coordinate of start of string ( see align )
337    cl      - color to draw the text in
338    fontnum - t1 library font id
339    points  - number of points in fontheight
340    str     - char pointer to string to render
341    len     - string length
342    align   - (0 - top of font glyph | 1 - baseline )
343
344 =cut
345 */
346
347 undef_int
348 i_t1_text(i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl,int fontnum, double points,const char* str,size_t len,int align, int utf8, char const *flags) {
349   GLYPH *glyph;
350   int xsize,ysize,y;
351   int mod_flags = t1_get_flags(flags);
352   i_render *r;
353
354   if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
355
356   if (utf8) {
357     int worklen;
358     char *work = t1_from_utf8(str, len, &worklen);
359     glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL);
360     myfree(work);
361   }
362   else {
363     /* T1_AASetString() accepts a char * not a const char */
364     glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
365   }
366   if (glyph == NULL)
367     return 0;
368
369   mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
370   mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
371   mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
372   mm_log((1,"bpp: %d\n",glyph->bpp));
373   
374   xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
375   ysize=glyph->metrics.ascent-glyph->metrics.descent;
376   
377   mm_log((1,"width: %d height: %d\n",xsize,ysize));
378
379   if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
380
381   r = i_render_new(im, xsize);
382   for(y=0;y<ysize;y++) {
383     i_render_color(r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
384   }
385   i_render_delete(r);
386     
387   return 1;
388 }
389
390 /*
391 =item t1_get_flags(flags)
392
393 Processes the characters in I<flags> to create a mod_flags value used
394 by some T1Lib functions.
395
396 =cut
397  */
398 static int
399 t1_get_flags(char const *flags) {
400   int mod_flags = T1_KERNING;
401
402   while (*flags) {
403     switch (*flags++) {
404     case 'u': case 'U': mod_flags |= T1_UNDERLINE; break;
405     case 'o': case 'O': mod_flags |= T1_OVERLINE; break;
406     case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break;
407       /* ignore anything we don't recognize */
408     }
409   }
410
411   return mod_flags;
412 }
413
414 /*
415 =item t1_from_utf8(char const *in, size_t len, int *outlen)
416
417 Produces an unencoded version of I<in> by dropping any Unicode
418 character over 255.
419
420 Returns a newly allocated buffer which should be freed with myfree().
421 Sets *outlen to the number of bytes used in the output string.
422
423 =cut
424 */
425
426 static char *
427 t1_from_utf8(char const *in, size_t len, int *outlen) {
428   /* at this point len is from a STRLEN which should be size_t and can't
429      be too big for mymalloc */
430   char *out = mymalloc(len+1); /* rechecked 29jul11 tonyc */
431   char *p = out;
432   unsigned long c;
433
434   while (len) {
435     c = i_utf8_advance(&in, &len);
436     if (c == ~0UL) {
437       myfree(out);
438       i_push_error(0, "invalid UTF8 character");
439       return 0;
440     }
441     /* yeah, just drop them */
442     if (c < 0x100) {
443       *p++ = (char)c;
444     }
445   }
446   *p = '\0';
447   *outlen = p - out;
448
449   return out;
450 }
451
452 /*
453 =item i_t1_has_chars(font_num, text, len, utf8, out)
454
455 Check if the given characters are defined by the font.  Note that len
456 is the number of bytes, not the number of characters (when utf8 is
457 non-zero).
458
459 out[char index] will be true if the character exists.
460
461 Accepts UTF-8, but since T1 can only have 256 characters, any chars
462 with values over 255 will simply be returned as false.
463
464 Returns the number of characters that were checked.
465
466 =cut
467 */
468
469 int
470 i_t1_has_chars(int font_num, const char *text, size_t len, int utf8,
471                char *out) {
472   int count = 0;
473   
474   mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", 
475           font_num, text, len, utf8));
476
477   i_clear_error();
478   if (T1_LoadFont(font_num)) {
479     t1_push_error();
480     return 0;
481   }
482
483   while (len) {
484     unsigned long c;
485     if (utf8) {
486       c = i_utf8_advance(&text, &len);
487       if (c == ~0UL) {
488         i_push_error(0, "invalid UTF8 character");
489         return 0;
490       }
491     }
492     else {
493       c = (unsigned char)*text++;
494       --len;
495     }
496     
497     if (c >= 0x100) {
498       /* limit of 256 characters for T1 */
499       *out++ = 0;
500     }
501     else {
502       char const * name = T1_GetCharName(font_num, (unsigned char)c);
503
504       if (name) {
505         *out++ = strcmp(name, ".notdef") != 0;
506       }
507       else {
508         mm_log((2, "  No name found for character %lx\n", c));
509         *out++ = 0;
510       }
511     }
512     ++count;
513   }
514
515   return count;
516 }
517
518 /*
519 =item i_t1_face_name(font_num, name_buf, name_buf_size)
520
521 Copies the face name of the given C<font_num> to C<name_buf>.  Returns
522 the number of characters required to store the name (which can be
523 larger than C<name_buf_size>, including the space required to store
524 the terminating NUL).
525
526 If name_buf is too small (as specified by name_buf_size) then the name
527 will be truncated.  name_buf will always be NUL termintaed.
528
529 =cut
530 */
531
532 int
533 i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
534   char *name;
535
536   T1_errno = 0;
537   if (T1_LoadFont(font_num)) {
538     t1_push_error();
539     return 0;
540   }
541   name = T1_GetFontName(font_num);
542
543   if (name) {
544     strncpy(name_buf, name, name_buf_size);
545     name_buf[name_buf_size-1] = '\0';
546     return strlen(name) + 1;
547   }
548   else {
549     t1_push_error();
550     return 0;
551   }
552 }
553
554 int
555 i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, 
556                  size_t name_buf_size) {
557   char *name;
558
559   i_clear_error();
560   if (ch > 0xFF) {
561     return 0;
562   }
563   if (T1_LoadFont(font_num)) {
564     t1_push_error();
565     return 0;
566   }
567   name = T1_GetCharName(font_num, (unsigned char)ch);
568   if (name) {
569     if (strcmp(name, ".notdef")) {
570       strncpy(name_buf, name, name_buf_size);
571       name_buf[name_buf_size-1] = '\0';
572       return strlen(name) + 1;
573     }
574     else {
575       return 0;
576     }
577   }
578   else {
579     t1_push_error();
580     return 0;
581   }
582 }
583
584 static void
585 t1_push_error(void) {
586 #if T1LIB_VERSION > 5 || T1LIB_VERSION == 5 && T1LIB_VERSION >= 1
587   /* I don't know when T1_StrError() was introduced, be conservative */
588   i_push_error(T1_errno, T1_StrError(T1_errno));
589 #else
590   switch (T1_errno) {
591   case 0: 
592     i_push_error(0, "No error"); 
593     break;
594
595 #ifdef T1ERR_SCAN_FONT_FORMAT
596   case T1ERR_SCAN_FONT_FORMAT:
597     i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); 
598     break;
599 #endif
600
601 #ifdef T1ERR_SCAN_FILE_OPEN_ERR
602   case T1ERR_SCAN_FILE_OPEN_ERR:
603     i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); 
604     break;
605 #endif
606
607 #ifdef T1ERR_SCAN_OUT_OF_MEMORY
608   case T1ERR_SCAN_OUT_OF_MEMORY:
609     i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); 
610     break;
611 #endif
612
613 #ifdef T1ERR_SCAN_ERROR
614   case T1ERR_SCAN_ERROR:
615     i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); 
616     break;
617 #endif
618
619 #ifdef T1ERR_SCAN_FILE_EOF
620   case T1ERR_SCAN_FILE_EOF:
621     i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); 
622     break;
623 #endif
624
625 #ifdef T1ERR_PATH_ERROR
626   case T1ERR_PATH_ERROR:
627     i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); 
628     break;
629 #endif
630
631 #ifdef T1ERR_PARSE_ERROR
632   case T1ERR_PARSE_ERROR:
633     i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); 
634     break;
635 #endif
636
637 #ifdef T1ERR_TYPE1_ABORT
638   case T1ERR_TYPE1_ABORT:
639     i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); 
640     break;
641 #endif
642
643 #ifdef T1ERR_INVALID_FONTID
644   case T1ERR_INVALID_FONTID:
645     i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); 
646     break;
647 #endif
648
649 #ifdef T1ERR_INVALID_PARAMETER
650   case T1ERR_INVALID_PARAMETER:
651     i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); 
652     break;
653 #endif
654
655 #ifdef T1ERR_OP_NOT_PERMITTED
656   case T1ERR_OP_NOT_PERMITTED:
657     i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); 
658     break;
659 #endif
660
661 #ifdef T1ERR_ALLOC_MEM
662   case T1ERR_ALLOC_MEM:
663     i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); 
664     break;
665 #endif
666
667 #ifdef T1ERR_FILE_OPEN_ERR
668   case T1ERR_FILE_OPEN_ERR:
669     i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); 
670     break;
671 #endif
672
673 #ifdef T1ERR_UNSPECIFIED
674   case T1ERR_UNSPECIFIED:
675     i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); 
676     break;
677 #endif
678
679 #ifdef T1ERR_NO_AFM_DATA
680   case T1ERR_NO_AFM_DATA:
681     i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); 
682     break;
683 #endif
684
685 #ifdef T1ERR_X11
686   case T1ERR_X11:
687     i_push_error(T1ERR_X11, "X11"); 
688     break;
689 #endif
690
691 #ifdef T1ERR_COMPOSITE_CHAR
692   case T1ERR_COMPOSITE_CHAR:
693     i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); 
694     break;
695 #endif
696
697   default:
698     i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
699   }
700 #endif
701 }
702