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