Commit | Line | Data |
---|---|---|
a556912d TC |
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); | |
1aaae40f | 8 | static undef_int i_init_t1_low(int t1log); |
a556912d | 9 | static void t1_push_error(void); |
1aaae40f | 10 | static void i_t1_set_aa(int st); |
a556912d TC |
11 | |
12 | static int t1_active_fonts = 0; | |
13 | static int t1_initialized = 0; | |
0d052fd9 | 14 | static int t1_aa = -1; |
1aaae40f TC |
15 | |
16 | struct i_t1_font_tag { | |
17 | int font_id; | |
18 | }; | |
19 | ||
20 | static i_mutex_t mutex; | |
21 | ||
22 | /* | |
23 | =item i_t1_start() | |
24 | ||
25 | Initialize the font driver. This does not actually initialize T1Lib, | |
26 | it just allocates the mutex we use to gate access to it. | |
27 | ||
28 | =cut | |
29 | */ | |
30 | ||
31 | void | |
32 | i_t1_start(void) { | |
33 | mutex = i_mutex_new(); | |
34 | } | |
a556912d TC |
35 | |
36 | /* | |
37 | =item i_init_t1(t1log) | |
38 | ||
39 | Initializes the t1lib font rendering engine. | |
40 | ||
41 | =cut | |
42 | */ | |
43 | ||
44 | undef_int | |
45 | i_init_t1(int t1log) { | |
1aaae40f TC |
46 | undef_int result; |
47 | i_mutex_lock(mutex); | |
48 | ||
49 | result = i_init_t1_low(t1log); | |
50 | ||
51 | i_mutex_unlock(mutex); | |
52 | ||
53 | return result; | |
54 | } | |
55 | ||
56 | static undef_int | |
57 | i_init_t1_low(int t1log) { | |
a556912d | 58 | int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE; |
5cb09ff7 TC |
59 | |
60 | mm_log((1,"init_t1(%d)\n", t1log)); | |
a556912d TC |
61 | |
62 | i_clear_error(); | |
63 | ||
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"); | |
67 | return 1; | |
68 | } | |
69 | ||
70 | if (t1_initialized) { | |
71 | T1_CloseLib(); | |
a3fd7df7 | 72 | t1_initialized = 0; |
a556912d TC |
73 | } |
74 | ||
75 | if (t1log) | |
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"); | |
80 | return(1); | |
81 | } | |
82 | T1_SetLogLevel(T1LOG_DEBUG); | |
a556912d TC |
83 | |
84 | ++t1_initialized; | |
85 | ||
86 | return(0); | |
87 | } | |
88 | ||
89 | /* | |
90 | =item i_close_t1() | |
91 | ||
92 | Shuts the t1lib font rendering engine down. | |
93 | ||
94 | This it seems that this function is never used. | |
95 | ||
96 | =cut | |
97 | */ | |
98 | ||
99 | void | |
100 | i_close_t1(void) { | |
1aaae40f | 101 | i_mutex_lock(mutex); |
a556912d TC |
102 | T1_CloseLib(); |
103 | t1_initialized = 0; | |
1aaae40f | 104 | i_mutex_unlock(mutex); |
a556912d TC |
105 | } |
106 | ||
107 | ||
108 | /* | |
109 | =item i_t1_new(pfb, afm) | |
110 | ||
111 | Loads the fonts with the given filenames, returns its font id | |
112 | ||
113 | pfb - path to pfb file for font | |
114 | afm - path to afm file for font | |
115 | ||
116 | =cut | |
117 | */ | |
118 | ||
1aaae40f | 119 | i_t1_font_t |
a556912d TC |
120 | i_t1_new(char *pfb,char *afm) { |
121 | int font_id; | |
1aaae40f TC |
122 | i_t1_font_t font; |
123 | ||
124 | i_mutex_lock(mutex); | |
a556912d TC |
125 | |
126 | i_clear_error(); | |
127 | ||
1aaae40f TC |
128 | if (!t1_initialized && i_init_t1_low(0)) { |
129 | i_mutex_unlock(mutex); | |
130 | return NULL; | |
131 | } | |
a556912d TC |
132 | |
133 | mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL"))); | |
134 | font_id = T1_AddFont(pfb); | |
135 | if (font_id<0) { | |
136 | mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id)); | |
73e3ff55 | 137 | t1_push_error(); |
1aaae40f TC |
138 | i_mutex_unlock(mutex); |
139 | return NULL; | |
a556912d TC |
140 | } |
141 | ||
142 | if (afm != NULL) { | |
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)); | |
145 | } | |
146 | ||
73e3ff55 TC |
147 | if (T1_LoadFont(font_id)) { |
148 | mm_log((1, "i_t1_new() -> -1 - T1_LoadFont failed (%d)\n", T1_errno)); | |
149 | t1_push_error(); | |
150 | i_push_error(0, "loading font"); | |
151 | T1_DeleteFont(font_id); | |
1aaae40f TC |
152 | i_mutex_unlock(mutex); |
153 | return NULL; | |
73e3ff55 TC |
154 | } |
155 | ||
a556912d TC |
156 | ++t1_active_fonts; |
157 | ||
1aaae40f TC |
158 | i_mutex_unlock(mutex); |
159 | ||
160 | font = mymalloc(sizeof(*font)); | |
161 | font->font_id = font_id; | |
162 | ||
5cb09ff7 TC |
163 | mm_log((1, "i_t1_new() -> %p (%d)\n", font, font_id)); |
164 | ||
1aaae40f | 165 | return font; |
a556912d TC |
166 | } |
167 | ||
168 | /* | |
1aaae40f | 169 | =item i_t1_destroy(font) |
a556912d TC |
170 | |
171 | Frees resources for a t1 font with given font id. | |
172 | ||
1aaae40f | 173 | font - font to free |
a556912d TC |
174 | |
175 | =cut | |
176 | */ | |
177 | ||
178 | int | |
1aaae40f TC |
179 | i_t1_destroy(i_t1_font_t font) { |
180 | int result; | |
181 | ||
182 | i_mutex_lock(mutex); | |
183 | ||
5cb09ff7 | 184 | mm_log((1,"i_t1_destroy(font %p (%d))\n", font, font->font_id)); |
a556912d TC |
185 | |
186 | --t1_active_fonts; | |
187 | ||
1aaae40f TC |
188 | result = T1_DeleteFont(font->font_id); |
189 | myfree(font); | |
190 | ||
191 | i_mutex_unlock(mutex); | |
192 | ||
193 | return result; | |
a556912d TC |
194 | } |
195 | ||
196 | ||
197 | /* | |
198 | =item i_t1_set_aa(st) | |
199 | ||
200 | Sets the antialiasing level of the t1 library. | |
201 | ||
202 | st - 0 = NONE, 1 = LOW, 2 = HIGH. | |
203 | ||
1aaae40f TC |
204 | Must be called with the mutex locked. |
205 | ||
a556912d TC |
206 | =cut |
207 | */ | |
208 | ||
1aaae40f | 209 | static void |
a556912d TC |
210 | i_t1_set_aa(int st) { |
211 | int i; | |
212 | unsigned long cst[17]; | |
1aaae40f | 213 | |
0d052fd9 TC |
214 | mm_log((1, "i_t1_set_aa(%d)\n", st)); |
215 | ||
1aaae40f TC |
216 | if (t1_aa == st) |
217 | return; | |
218 | ||
a556912d TC |
219 | switch(st) { |
220 | case 0: | |
221 | T1_AASetBitsPerPixel( 8 ); | |
222 | T1_AASetLevel( T1_AA_NONE ); | |
223 | T1_AANSetGrayValues( 0, 255 ); | |
224 | mm_log((1,"setting T1 antialias to none\n")); | |
225 | break; | |
226 | case 1: | |
227 | T1_AASetBitsPerPixel( 8 ); | |
228 | T1_AASetLevel( T1_AA_LOW ); | |
229 | T1_AASetGrayValues( 0,65,127,191,255 ); | |
230 | mm_log((1,"setting T1 antialias to low\n")); | |
231 | break; | |
232 | case 2: | |
233 | T1_AASetBitsPerPixel(8); | |
234 | T1_AASetLevel(T1_AA_HIGH); | |
235 | for(i=0;i<17;i++) cst[i]=(i*255)/16; | |
236 | T1_AAHSetGrayValues( cst ); | |
237 | mm_log((1,"setting T1 antialias to high\n")); | |
238 | } | |
1aaae40f TC |
239 | |
240 | t1_aa = st; | |
a556912d TC |
241 | } |
242 | ||
243 | ||
244 | /* | |
1aaae40f | 245 | =item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align,aa) |
a556912d TC |
246 | |
247 | Interface to text rendering into a single channel in an image | |
248 | ||
249 | im pointer to image structure | |
250 | xb x coordinate of start of string | |
251 | yb y coordinate of start of string ( see align ) | |
252 | channel - destination channel | |
253 | fontnum - t1 library font id | |
254 | points - number of points in fontheight | |
255 | str - string to render | |
256 | len - string length | |
257 | align - (0 - top of font glyph | 1 - baseline ) | |
5cb09ff7 | 258 | aa - anti-aliasing level |
a556912d TC |
259 | |
260 | =cut | |
261 | */ | |
262 | ||
263 | undef_int | |
1aaae40f | 264 | 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) { |
a556912d TC |
265 | GLYPH *glyph; |
266 | int xsize,ysize,x,y; | |
267 | i_color val; | |
268 | int mod_flags = t1_get_flags(flags); | |
1aaae40f | 269 | int fontnum = font->font_id; |
a556912d TC |
270 | |
271 | unsigned int ch_mask_store; | |
272 | ||
5cb09ff7 TC |
273 | i_clear_error(); |
274 | ||
275 | 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", | |
276 | font, fontnum, im, i_DFcp(xb, yb), channel, points, str, (unsigned)len, align, utf8, flags, aa)); | |
277 | ||
278 | if (im == NULL) { | |
279 | mm_log((1,"i_t1_cp: Null image in input\n")); | |
280 | i_push_error(0, "null image"); | |
281 | return(0); | |
282 | } | |
a2f0c379 | 283 | |
1aaae40f TC |
284 | i_mutex_lock(mutex); |
285 | ||
286 | i_t1_set_aa(aa); | |
287 | ||
a556912d TC |
288 | if (utf8) { |
289 | int worklen; | |
290 | char *work = t1_from_utf8(str, len, &worklen); | |
932f9afb TC |
291 | if (work == NULL) { |
292 | i_mutex_unlock(mutex); | |
293 | return 0; | |
294 | } | |
a556912d TC |
295 | glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); |
296 | myfree(work); | |
297 | } | |
298 | else { | |
299 | glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL); | |
300 | } | |
1aaae40f | 301 | if (glyph == NULL) { |
5cb09ff7 TC |
302 | t1_push_error(); |
303 | i_push_error(0, "i_t1_cp: T1_AASetString failed"); | |
1aaae40f | 304 | i_mutex_unlock(mutex); |
a556912d | 305 | return 0; |
1aaae40f | 306 | } |
a556912d TC |
307 | |
308 | mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); | |
309 | mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); | |
310 | mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); | |
5e84d110 | 311 | mm_log((1,"bpp: %lu\n", (unsigned long)glyph->bpp)); |
a556912d TC |
312 | |
313 | xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; | |
314 | ysize=glyph->metrics.ascent-glyph->metrics.descent; | |
315 | ||
316 | mm_log((1,"width: %d height: %d\n",xsize,ysize)); | |
317 | ||
318 | ch_mask_store=im->ch_mask; | |
319 | im->ch_mask=1<<channel; | |
320 | ||
321 | if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; } | |
322 | ||
323 | for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) { | |
324 | val.channel[channel]=glyph->bits[y*xsize+x]; | |
325 | i_ppix(im,x+xb,y+yb,&val); | |
326 | } | |
327 | ||
328 | im->ch_mask=ch_mask_store; | |
1aaae40f TC |
329 | |
330 | i_mutex_unlock(mutex); | |
331 | ||
a556912d TC |
332 | return 1; |
333 | } | |
334 | ||
335 | static void | |
336 | t1_fix_bbox(BBox *bbox, const char *str, size_t len, int advance, | |
337 | int space_position) { | |
338 | /* never called with len == 0 */ | |
339 | if (str[0] == space_position && bbox->llx > 0) | |
340 | bbox->llx = 0; | |
341 | if (str[len-1] == space_position && bbox->urx < advance) | |
342 | bbox->urx = advance; | |
343 | if (bbox->lly > bbox->ury) | |
344 | bbox->lly = bbox->ury = 0; | |
345 | } | |
346 | ||
347 | /* | |
348 | =item i_t1_bbox(handle, fontnum, points, str, len, cords) | |
349 | ||
350 | function to get a strings bounding box given the font id and sizes | |
351 | ||
352 | handle - pointer to font handle | |
353 | fontnum - t1 library font id | |
354 | points - number of points in fontheight | |
355 | str - string to measure | |
356 | len - string length | |
357 | cords - the bounding box (modified in place) | |
358 | ||
359 | =cut | |
360 | */ | |
361 | ||
362 | int | |
567c2038 | 363 | i_t1_bbox(i_t1_font_t font, double points,const char *str,size_t len, i_img_dim *cords, int utf8,char const *flags) { |
a556912d TC |
364 | BBox bbox; |
365 | BBox gbbox; | |
366 | int mod_flags = t1_get_flags(flags); | |
8d14daab | 367 | i_img_dim advance; |
1aaae40f TC |
368 | int fontnum = font->font_id; |
369 | int space_position; | |
370 | ||
932f9afb TC |
371 | i_clear_error(); |
372 | ||
1aaae40f TC |
373 | i_mutex_lock(mutex); |
374 | ||
375 | space_position = T1_GetEncodingIndex(fontnum, "space"); | |
a556912d | 376 | |
5e84d110 TC |
377 | mm_log((1,"i_t1_bbox(font %p (%d),points %.2f,str '%.*s', len %u)\n", |
378 | font, fontnum,points,(int)len,str,(unsigned)len)); | |
932f9afb TC |
379 | if (T1_LoadFont(fontnum) == -1) { |
380 | t1_push_error(); | |
381 | i_mutex_unlock(mutex); | |
382 | return 0; | |
383 | } | |
a556912d TC |
384 | |
385 | if (len == 0) { | |
386 | /* len == 0 has special meaning to T1lib, but it means there's | |
387 | nothing to draw, so return that */ | |
388 | bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0; | |
389 | advance = 0; | |
390 | } | |
391 | else { | |
392 | if (utf8) { | |
393 | int worklen; | |
394 | char *work = t1_from_utf8(str, len, &worklen); | |
932f9afb TC |
395 | if (!work) { |
396 | i_mutex_unlock(mutex); | |
397 | return 0; | |
398 | } | |
a556912d TC |
399 | advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags); |
400 | bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags); | |
401 | t1_fix_bbox(&bbox, work, worklen, advance, space_position); | |
402 | myfree(work); | |
403 | } | |
404 | else { | |
405 | advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags); | |
406 | bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags); | |
407 | t1_fix_bbox(&bbox, str, len, advance, space_position); | |
408 | } | |
409 | } | |
410 | gbbox = T1_GetFontBBox(fontnum); | |
411 | ||
5e84d110 | 412 | mm_log((1,"bbox: (%d, %d, %d, %d, %d, %d)\n", |
a556912d TC |
413 | (int)(bbox.llx*points/1000), |
414 | (int)(gbbox.lly*points/1000), | |
415 | (int)(bbox.urx*points/1000), | |
416 | (int)(gbbox.ury*points/1000), | |
417 | (int)(bbox.lly*points/1000), | |
418 | (int)(bbox.ury*points/1000) )); | |
419 | ||
420 | ||
8d14daab TC |
421 | cords[BBOX_NEG_WIDTH]=((double)bbox.llx*points)/1000; |
422 | cords[BBOX_POS_WIDTH]=((double)bbox.urx*points)/1000; | |
a556912d | 423 | |
8d14daab TC |
424 | cords[BBOX_GLOBAL_DESCENT]=((double)gbbox.lly*points)/1000; |
425 | cords[BBOX_GLOBAL_ASCENT]=((double)gbbox.ury*points)/1000; | |
a556912d | 426 | |
8d14daab TC |
427 | cords[BBOX_DESCENT]=((double)bbox.lly*points)/1000; |
428 | cords[BBOX_ASCENT]=((double)bbox.ury*points)/1000; | |
a556912d | 429 | |
8d14daab | 430 | cords[BBOX_ADVANCE_WIDTH] = ((double)advance * points)/1000; |
a556912d TC |
431 | cords[BBOX_RIGHT_BEARING] = |
432 | cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH]; | |
433 | ||
1aaae40f TC |
434 | i_mutex_unlock(mutex); |
435 | ||
a556912d TC |
436 | return BBOX_RIGHT_BEARING+1; |
437 | } | |
438 | ||
439 | ||
440 | /* | |
932f9afb | 441 | =item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align, utf8, flags, aa) |
a556912d TC |
442 | |
443 | Interface to text rendering in a single color onto an image | |
444 | ||
445 | im - pointer to image structure | |
446 | xb - x coordinate of start of string | |
447 | yb - y coordinate of start of string ( see align ) | |
448 | cl - color to draw the text in | |
449 | fontnum - t1 library font id | |
450 | points - number of points in fontheight | |
451 | str - char pointer to string to render | |
452 | len - string length | |
453 | align - (0 - top of font glyph | 1 - baseline ) | |
932f9afb TC |
454 | utf8 - str is utf8 |
455 | flags - formatting flags | |
1aaae40f | 456 | aa - anti-aliasing level |
a556912d TC |
457 | |
458 | =cut | |
459 | */ | |
460 | ||
461 | undef_int | |
1aaae40f | 462 | 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) { |
a556912d TC |
463 | GLYPH *glyph; |
464 | int xsize,ysize,y; | |
465 | int mod_flags = t1_get_flags(flags); | |
466 | i_render *r; | |
1aaae40f | 467 | int fontnum = font->font_id; |
a556912d | 468 | |
5cb09ff7 TC |
469 | 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", |
470 | 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)); | |
471 | ||
472 | i_clear_error(); | |
473 | ||
474 | if (im == NULL) { | |
475 | i_push_error(0, "null image"); | |
476 | mm_log((1,"i_t1_text: Null image in input\n")); | |
477 | return(0); | |
478 | } | |
a2f0c379 | 479 | |
1aaae40f TC |
480 | i_mutex_lock(mutex); |
481 | ||
482 | i_t1_set_aa(aa); | |
483 | ||
a556912d TC |
484 | if (utf8) { |
485 | int worklen; | |
486 | char *work = t1_from_utf8(str, len, &worklen); | |
932f9afb TC |
487 | if (!work) { |
488 | i_mutex_unlock(mutex); | |
489 | return 0; | |
490 | } | |
a556912d TC |
491 | glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); |
492 | myfree(work); | |
493 | } | |
494 | else { | |
495 | /* T1_AASetString() accepts a char * not a const char */ | |
496 | glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL); | |
497 | } | |
1aaae40f | 498 | if (glyph == NULL) { |
5cb09ff7 TC |
499 | mm_log((1, "T1_AASetString failed\n")); |
500 | t1_push_error(); | |
501 | i_push_error(0, "i_t1_text(): T1_AASetString failed"); | |
1aaae40f | 502 | i_mutex_unlock(mutex); |
a556912d | 503 | return 0; |
1aaae40f | 504 | } |
a556912d TC |
505 | |
506 | mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); | |
507 | mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); | |
508 | mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); | |
5e84d110 | 509 | mm_log((1,"bpp: %lu\n",(unsigned long)glyph->bpp)); |
a556912d TC |
510 | |
511 | xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; | |
512 | ysize=glyph->metrics.ascent-glyph->metrics.descent; | |
513 | ||
514 | mm_log((1,"width: %d height: %d\n",xsize,ysize)); | |
515 | ||
516 | if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; } | |
517 | ||
518 | r = i_render_new(im, xsize); | |
519 | for(y=0;y<ysize;y++) { | |
520 | i_render_color(r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl); | |
521 | } | |
522 | i_render_delete(r); | |
1aaae40f TC |
523 | |
524 | i_mutex_unlock(mutex); | |
a556912d TC |
525 | |
526 | return 1; | |
527 | } | |
528 | ||
529 | /* | |
530 | =item t1_get_flags(flags) | |
531 | ||
532 | Processes the characters in I<flags> to create a mod_flags value used | |
533 | by some T1Lib functions. | |
534 | ||
535 | =cut | |
536 | */ | |
537 | static int | |
538 | t1_get_flags(char const *flags) { | |
539 | int mod_flags = T1_KERNING; | |
540 | ||
541 | while (*flags) { | |
542 | switch (*flags++) { | |
543 | case 'u': case 'U': mod_flags |= T1_UNDERLINE; break; | |
544 | case 'o': case 'O': mod_flags |= T1_OVERLINE; break; | |
545 | case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break; | |
546 | /* ignore anything we don't recognize */ | |
547 | } | |
548 | } | |
549 | ||
550 | return mod_flags; | |
551 | } | |
552 | ||
553 | /* | |
554 | =item t1_from_utf8(char const *in, size_t len, int *outlen) | |
555 | ||
556 | Produces an unencoded version of I<in> by dropping any Unicode | |
557 | character over 255. | |
558 | ||
559 | Returns a newly allocated buffer which should be freed with myfree(). | |
560 | Sets *outlen to the number of bytes used in the output string. | |
561 | ||
562 | =cut | |
563 | */ | |
564 | ||
565 | static char * | |
566 | t1_from_utf8(char const *in, size_t len, int *outlen) { | |
8d14daab TC |
567 | /* at this point len is from a STRLEN which should be size_t and can't |
568 | be too big for mymalloc */ | |
569 | char *out = mymalloc(len+1); /* rechecked 29jul11 tonyc */ | |
a556912d TC |
570 | char *p = out; |
571 | unsigned long c; | |
572 | ||
573 | while (len) { | |
574 | c = i_utf8_advance(&in, &len); | |
575 | if (c == ~0UL) { | |
576 | myfree(out); | |
577 | i_push_error(0, "invalid UTF8 character"); | |
578 | return 0; | |
579 | } | |
580 | /* yeah, just drop them */ | |
581 | if (c < 0x100) { | |
582 | *p++ = (char)c; | |
583 | } | |
584 | } | |
585 | *p = '\0'; | |
586 | *outlen = p - out; | |
587 | ||
588 | return out; | |
589 | } | |
590 | ||
591 | /* | |
592 | =item i_t1_has_chars(font_num, text, len, utf8, out) | |
593 | ||
594 | Check if the given characters are defined by the font. Note that len | |
595 | is the number of bytes, not the number of characters (when utf8 is | |
596 | non-zero). | |
597 | ||
598 | out[char index] will be true if the character exists. | |
599 | ||
600 | Accepts UTF-8, but since T1 can only have 256 characters, any chars | |
601 | with values over 255 will simply be returned as false. | |
602 | ||
603 | Returns the number of characters that were checked. | |
604 | ||
605 | =cut | |
606 | */ | |
607 | ||
608 | int | |
1aaae40f | 609 | i_t1_has_chars(i_t1_font_t font, const char *text, size_t len, int utf8, |
a556912d TC |
610 | char *out) { |
611 | int count = 0; | |
1aaae40f | 612 | int font_num = font->font_id; |
a556912d | 613 | |
1aaae40f TC |
614 | i_mutex_lock(mutex); |
615 | ||
5e84d110 TC |
616 | mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %u, utf8 %d)\n", |
617 | font_num, text, (unsigned)len, utf8)); | |
a556912d TC |
618 | |
619 | i_clear_error(); | |
620 | if (T1_LoadFont(font_num)) { | |
621 | t1_push_error(); | |
1aaae40f | 622 | i_mutex_unlock(mutex); |
a556912d TC |
623 | return 0; |
624 | } | |
625 | ||
626 | while (len) { | |
627 | unsigned long c; | |
628 | if (utf8) { | |
629 | c = i_utf8_advance(&text, &len); | |
630 | if (c == ~0UL) { | |
631 | i_push_error(0, "invalid UTF8 character"); | |
1aaae40f | 632 | i_mutex_unlock(mutex); |
a556912d TC |
633 | return 0; |
634 | } | |
635 | } | |
636 | else { | |
637 | c = (unsigned char)*text++; | |
638 | --len; | |
639 | } | |
640 | ||
641 | if (c >= 0x100) { | |
642 | /* limit of 256 characters for T1 */ | |
643 | *out++ = 0; | |
644 | } | |
645 | else { | |
646 | char const * name = T1_GetCharName(font_num, (unsigned char)c); | |
647 | ||
648 | if (name) { | |
649 | *out++ = strcmp(name, ".notdef") != 0; | |
650 | } | |
651 | else { | |
652 | mm_log((2, " No name found for character %lx\n", c)); | |
653 | *out++ = 0; | |
654 | } | |
655 | } | |
656 | ++count; | |
657 | } | |
658 | ||
1aaae40f TC |
659 | i_mutex_unlock(mutex); |
660 | ||
a556912d TC |
661 | return count; |
662 | } | |
663 | ||
664 | /* | |
1aaae40f | 665 | =item i_t1_face_name(font, name_buf, name_buf_size) |
a556912d TC |
666 | |
667 | Copies the face name of the given C<font_num> to C<name_buf>. Returns | |
668 | the number of characters required to store the name (which can be | |
669 | larger than C<name_buf_size>, including the space required to store | |
670 | the terminating NUL). | |
671 | ||
672 | If name_buf is too small (as specified by name_buf_size) then the name | |
673 | will be truncated. name_buf will always be NUL termintaed. | |
674 | ||
675 | =cut | |
676 | */ | |
677 | ||
678 | int | |
1aaae40f | 679 | i_t1_face_name(i_t1_font_t font, char *name_buf, size_t name_buf_size) { |
a556912d | 680 | char *name; |
1aaae40f TC |
681 | int font_num = font->font_id; |
682 | ||
683 | i_mutex_lock(mutex); | |
a556912d TC |
684 | |
685 | T1_errno = 0; | |
686 | if (T1_LoadFont(font_num)) { | |
687 | t1_push_error(); | |
1aaae40f | 688 | i_mutex_unlock(mutex); |
a556912d TC |
689 | return 0; |
690 | } | |
691 | name = T1_GetFontName(font_num); | |
692 | ||
693 | if (name) { | |
1aaae40f | 694 | size_t len = strlen(name); |
a556912d TC |
695 | strncpy(name_buf, name, name_buf_size); |
696 | name_buf[name_buf_size-1] = '\0'; | |
1aaae40f TC |
697 | i_mutex_unlock(mutex); |
698 | return len + 1; | |
a556912d TC |
699 | } |
700 | else { | |
701 | t1_push_error(); | |
1aaae40f | 702 | i_mutex_unlock(mutex); |
a556912d TC |
703 | return 0; |
704 | } | |
705 | } | |
706 | ||
707 | int | |
1aaae40f | 708 | i_t1_glyph_name(i_t1_font_t font, unsigned long ch, char *name_buf, |
a556912d TC |
709 | size_t name_buf_size) { |
710 | char *name; | |
1aaae40f | 711 | int font_num = font->font_id; |
a556912d TC |
712 | |
713 | i_clear_error(); | |
714 | if (ch > 0xFF) { | |
715 | return 0; | |
716 | } | |
932f9afb TC |
717 | |
718 | i_mutex_lock(mutex); | |
719 | ||
a556912d TC |
720 | if (T1_LoadFont(font_num)) { |
721 | t1_push_error(); | |
1aaae40f | 722 | i_mutex_unlock(mutex); |
a556912d TC |
723 | return 0; |
724 | } | |
725 | name = T1_GetCharName(font_num, (unsigned char)ch); | |
726 | if (name) { | |
727 | if (strcmp(name, ".notdef")) { | |
1aaae40f | 728 | size_t len = strlen(name); |
a556912d TC |
729 | strncpy(name_buf, name, name_buf_size); |
730 | name_buf[name_buf_size-1] = '\0'; | |
1aaae40f TC |
731 | i_mutex_unlock(mutex); |
732 | return len + 1; | |
a556912d TC |
733 | } |
734 | else { | |
1aaae40f | 735 | i_mutex_unlock(mutex); |
a556912d TC |
736 | return 0; |
737 | } | |
738 | } | |
739 | else { | |
740 | t1_push_error(); | |
1aaae40f | 741 | i_mutex_unlock(mutex); |
a556912d TC |
742 | return 0; |
743 | } | |
744 | } | |
745 | ||
746 | static void | |
747 | t1_push_error(void) { | |
73e3ff55 TC |
748 | #if T1LIB_VERSION > 5 || T1LIB_VERSION == 5 && T1LIB_VERSION >= 1 |
749 | /* I don't know when T1_StrError() was introduced, be conservative */ | |
750 | i_push_error(T1_errno, T1_StrError(T1_errno)); | |
751 | #else | |
a556912d TC |
752 | switch (T1_errno) { |
753 | case 0: | |
754 | i_push_error(0, "No error"); | |
755 | break; | |
756 | ||
757 | #ifdef T1ERR_SCAN_FONT_FORMAT | |
758 | case T1ERR_SCAN_FONT_FORMAT: | |
959df113 | 759 | i_push_error(T1ERR_SCAN_FONT_FORMAT, "Attempt to Load Multiple Master Font"); |
a556912d TC |
760 | break; |
761 | #endif | |
762 | ||
763 | #ifdef T1ERR_SCAN_FILE_OPEN_ERR | |
764 | case T1ERR_SCAN_FILE_OPEN_ERR: | |
959df113 | 765 | i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "Type 1 Font File Open Error"); |
a556912d TC |
766 | break; |
767 | #endif | |
768 | ||
769 | #ifdef T1ERR_SCAN_OUT_OF_MEMORY | |
770 | case T1ERR_SCAN_OUT_OF_MEMORY: | |
959df113 | 771 | i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "Virtual Memory Exceeded"); |
a556912d TC |
772 | break; |
773 | #endif | |
774 | ||
775 | #ifdef T1ERR_SCAN_ERROR | |
776 | case T1ERR_SCAN_ERROR: | |
959df113 | 777 | i_push_error(T1ERR_SCAN_ERROR, "Syntactical Error Scanning Font File"); |
a556912d TC |
778 | break; |
779 | #endif | |
780 | ||
781 | #ifdef T1ERR_SCAN_FILE_EOF | |
782 | case T1ERR_SCAN_FILE_EOF: | |
959df113 | 783 | i_push_error(T1ERR_SCAN_FILE_EOF, "Premature End of Font File Encountered"); |
a556912d TC |
784 | break; |
785 | #endif | |
786 | ||
787 | #ifdef T1ERR_PATH_ERROR | |
788 | case T1ERR_PATH_ERROR: | |
959df113 | 789 | i_push_error(T1ERR_PATH_ERROR, "Path Construction Error"); |
a556912d TC |
790 | break; |
791 | #endif | |
792 | ||
793 | #ifdef T1ERR_PARSE_ERROR | |
794 | case T1ERR_PARSE_ERROR: | |
959df113 | 795 | i_push_error(T1ERR_PARSE_ERROR, "Font is Corrupt"); |
a556912d TC |
796 | break; |
797 | #endif | |
798 | ||
799 | #ifdef T1ERR_TYPE1_ABORT | |
800 | case T1ERR_TYPE1_ABORT: | |
959df113 | 801 | i_push_error(T1ERR_TYPE1_ABORT, "Rasterization Aborted"); |
a556912d TC |
802 | break; |
803 | #endif | |
804 | ||
805 | #ifdef T1ERR_INVALID_FONTID | |
806 | case T1ERR_INVALID_FONTID: | |
959df113 | 807 | i_push_error(T1ERR_INVALID_FONTID, "Font ID Invalid in this Context"); |
a556912d TC |
808 | break; |
809 | #endif | |
810 | ||
811 | #ifdef T1ERR_INVALID_PARAMETER | |
812 | case T1ERR_INVALID_PARAMETER: | |
959df113 | 813 | i_push_error(T1ERR_INVALID_PARAMETER, "Invalid Argument in Function Call"); |
a556912d TC |
814 | break; |
815 | #endif | |
816 | ||
817 | #ifdef T1ERR_OP_NOT_PERMITTED | |
818 | case T1ERR_OP_NOT_PERMITTED: | |
959df113 | 819 | i_push_error(T1ERR_OP_NOT_PERMITTED, "Operation not Permitted"); |
a556912d TC |
820 | break; |
821 | #endif | |
822 | ||
823 | #ifdef T1ERR_ALLOC_MEM | |
824 | case T1ERR_ALLOC_MEM: | |
959df113 | 825 | i_push_error(T1ERR_ALLOC_MEM, "Memory Allocation Error"); |
a556912d TC |
826 | break; |
827 | #endif | |
828 | ||
829 | #ifdef T1ERR_FILE_OPEN_ERR | |
830 | case T1ERR_FILE_OPEN_ERR: | |
959df113 | 831 | i_push_error(T1ERR_FILE_OPEN_ERR, "Error Opening File"); |
a556912d TC |
832 | break; |
833 | #endif | |
834 | ||
835 | #ifdef T1ERR_UNSPECIFIED | |
836 | case T1ERR_UNSPECIFIED: | |
959df113 | 837 | i_push_error(T1ERR_UNSPECIFIED, "Unspecified T1Lib Error"); |
a556912d TC |
838 | break; |
839 | #endif | |
840 | ||
841 | #ifdef T1ERR_NO_AFM_DATA | |
842 | case T1ERR_NO_AFM_DATA: | |
959df113 | 843 | i_push_error(T1ERR_NO_AFM_DATA, "Missing AFM Data"); |
a556912d TC |
844 | break; |
845 | #endif | |
846 | ||
847 | #ifdef T1ERR_X11 | |
848 | case T1ERR_X11: | |
959df113 | 849 | i_push_error(T1ERR_X11, "X11 Interface Error"); |
a556912d TC |
850 | break; |
851 | #endif | |
852 | ||
853 | #ifdef T1ERR_COMPOSITE_CHAR | |
854 | case T1ERR_COMPOSITE_CHAR: | |
959df113 TC |
855 | i_push_error(T1ERR_COMPOSITE_CHAR, "Missing Component of Composite Character"); |
856 | break; | |
857 | #endif | |
858 | ||
859 | #ifdef T1ERR_SCAN_ENCODING | |
860 | case T1ERR_SCAN_ENCODING: | |
861 | i_push_error(T1ERR_SCAN_ENCODING, "Error Scanning Encoding File"); | |
a556912d TC |
862 | break; |
863 | #endif | |
864 | ||
865 | default: | |
866 | i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno); | |
867 | } | |
73e3ff55 | 868 | #endif |
a556912d TC |
869 | } |
870 |