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