update from the separate T1 distribution
[imager.git] / T1 / imt1.c
CommitLineData
a556912d
TC
1#include "imext.h"
2#include "imt1.h"
3#include <t1lib.h>
4#include <string.h>
5
6static int t1_get_flags(char const *flags);
7static char *t1_from_utf8(char const *in, size_t len, int *outlen);
8
9static void t1_push_error(void);
10
11static int t1_active_fonts = 0;
12static int t1_initialized = 0;
13
14/*
15=item i_init_t1(t1log)
16
17Initializes the t1lib font rendering engine.
18
19=cut
20*/
21
22undef_int
23i_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
57Shuts the t1lib font rendering engine down.
58
59 This it seems that this function is never used.
60
61=cut
62*/
63
64void
65i_close_t1(void) {
66 T1_CloseLib();
67 t1_initialized = 0;
68}
69
70
71/*
72=item i_t1_new(pfb, afm)
73
74Loads 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
82int
83i_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
111Frees resources for a t1 font with given font id.
112
113 font_id - number of the font to free
114
115=cut
116*/
117
118int
119i_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
131Sets the antialiasing level of the t1 library.
132
133 st - 0 = NONE, 1 = LOW, 2 = HIGH.
134
135=cut
136*/
137
138void
139i_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
168Interface 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
183undef_int
184i_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
230static void
231t1_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
245function 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
257int
258i_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
320Interface 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
335undef_int
336i_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
381Processes the characters in I<flags> to create a mod_flags value used
382by some T1Lib functions.
383
384=cut
385 */
386static int
387t1_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
405Produces an unencoded version of I<in> by dropping any Unicode
406character over 255.
407
408Returns a newly allocated buffer which should be freed with myfree().
409Sets *outlen to the number of bytes used in the output string.
410
411=cut
412*/
413
414static char *
415t1_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
442Check if the given characters are defined by the font. Note that len
443is the number of bytes, not the number of characters (when utf8 is
444non-zero).
445
446out[char index] will be true if the character exists.
447
448Accepts UTF-8, but since T1 can only have 256 characters, any chars
449with values over 255 will simply be returned as false.
450
451Returns the number of characters that were checked.
452
453=cut
454*/
455
456int
457i_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
508Copies the face name of the given C<font_num> to C<name_buf>. Returns
509the number of characters required to store the name (which can be
510larger than C<name_buf_size>, including the space required to store
511the terminating NUL).
512
513If name_buf is too small (as specified by name_buf_size) then the name
514will be truncated. name_buf will always be NUL termintaed.
515
516=cut
517*/
518
519int
520i_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
541int
542i_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
571static void
572t1_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