1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "../ui_font.h"
4 
5 #include <pobl/bl_debug.h>
6 #include <pobl/bl_str.h>    /* bl_snprintf */
7 #include <pobl/bl_mem.h>    /* alloca */
8 #include <pobl/bl_str.h>    /* bl_str_sep/bl_str_to_int/memset/strncasecmp */
9 #include <pobl/bl_util.h>   /* DIGIT_STR_LEN */
10 #include <pobl/bl_locale.h> /* bl_get_lang() */
11 #include <mef/ef_ucs4_map.h>
12 #include <vt_char_encoding.h> /* vt_is_msb_set */
13 
14 #include "ui_type_loader.h"
15 #include "ui_decsp_font.h"
16 
17 #define FOREACH_FONT_ENCODINGS(csinfo, font_encoding_p)                                      \
18   for ((font_encoding_p) = (csinfo)->encoding_names;                                         \
19        (font_encoding_p) <                                                                   \
20                (csinfo)->encoding_names +                                                    \
21                    sizeof((csinfo)->encoding_names) / sizeof((csinfo)->encoding_names[0]) && \
22            *(font_encoding_p);                                                               \
23        (font_encoding_p)++)
24 
25 #define DIVIDE_ROUNDING(a, b) (((int)((a)*10 + (b)*5)) / ((int)((b)*10)))
26 #define DIVIDE_ROUNDINGUP(a, b) (((int)((a)*10 + (b)*10 - 1)) / ((int)((b)*10)))
27 
28 #if 0
29 #define __DEBUG
30 #endif
31 
32 typedef struct cs_info {
33   ef_charset_t cs;
34 
35   /* default encodings. */
36   char *encoding_names[2];
37 
38 } cs_info_t;
39 
40 /* --- static variables --- */
41 
42 /*
43  * If this table is changed, ui_font_config.c:cs_table and
44  * mc_font.c:cs_info_table
45  * shoule be also changed.
46  */
47 static cs_info_t cs_info_table[] = {
48   { ISO10646_UCS4_1, { "iso10646-1", NULL, }, },
49   { DEC_SPECIAL, { "iso8859-1", NULL, }, },
50   { ISO8859_1_R, { "iso8859-1", NULL, }, },
51   { ISO8859_2_R, { "iso8859-2", NULL, }, },
52   { ISO8859_3_R, { "iso8859-3", NULL, }, },
53   { ISO8859_4_R, { "iso8859-4", NULL, }, },
54   { ISO8859_5_R, { "iso8859-5", NULL, }, },
55   { ISO8859_6_R, { "iso8859-6", NULL, }, },
56   { ISO8859_7_R, { "iso8859-7", NULL, }, },
57   { ISO8859_8_R, { "iso8859-8", NULL, }, },
58   { ISO8859_9_R, { "iso8859-9", NULL, }, },
59   { ISO8859_10_R, { "iso8859-10", NULL, }, },
60   { TIS620_2533, { "tis620.2533-1", "tis620.2529-1", }, },
61   { ISO8859_13_R, { "iso8859-13", NULL, }, },
62   { ISO8859_14_R, { "iso8859-14", NULL, }, },
63   { ISO8859_15_R, { "iso8859-15", NULL, }, },
64   { ISO8859_16_R, { "iso8859-16", NULL, }, },
65 
66   /*
67    * XXX
68    * The encoding of TCVN font is iso8859-1, and its font family is .VnTime or
69    * .VnTimeH... How to deal with it ?
70    */
71   { TCVN5712_3_1993, { NULL, NULL, }, },
72 
73   { ISCII_ASSAMESE, { NULL, NULL, }, },
74   { ISCII_BENGALI, { NULL, NULL, }, },
75   { ISCII_GUJARATI, { NULL, NULL, }, },
76   { ISCII_HINDI, { NULL, NULL, }, },
77   { ISCII_KANNADA, { NULL, NULL, }, },
78   { ISCII_MALAYALAM, { NULL, NULL, }, },
79   { ISCII_ORIYA, { NULL, NULL, }, },
80   { ISCII_PUNJABI, { NULL, NULL, }, },
81   { ISCII_TAMIL, { NULL, NULL, }, },
82   { ISCII_TELUGU, { NULL, NULL, }, },
83   { VISCII, { "viscii-1", NULL, }, },
84   { KOI8_R, { "koi8-r", NULL, }, },
85   { KOI8_U, { "koi8-u", NULL, }, },
86 
87 #if 0
88   /*
89    * XXX
90    * KOI8_T, GEORGIAN_PS and CP125X charset can be shown by unicode font only.
91    */
92   { KOI8_T, { NULL, NULL, }, },
93   { GEORGIAN_PS, { NULL, NULL, }, },
94   { CP1250, { NULL, NULL, }, },
95   { CP1251, { NULL, NULL, }, },
96   { CP1252, { NULL, NULL, }, },
97   { CP1253, { NULL, NULL, }, },
98   { CP1254, { NULL, NULL, }, },
99   { CP1255, { NULL, NULL, }, },
100   { CP1256, { NULL, NULL, }, },
101   { CP1257, { NULL, NULL, }, },
102   { CP1258, { NULL, NULL, }, },
103   { CP874, { NULL, NULL, }, },
104 #endif
105 
106   { JISX0201_KATA, { "jisx0201.1976-0", NULL, }, },
107   { JISX0201_ROMAN, { "jisx0201.1976-0", NULL, }, },
108   { JISC6226_1978, { "jisx0208.1978-0", "jisx0208.1983-0", }, },
109   { JISX0208_1983, { "jisx0208.1983-0", "jisx0208.1990-0", }, },
110   { JISX0208_1990, { "jisx0208.1990-0", "jisx0208.1983-0", }, },
111   { JISX0212_1990, { "jisx0212.1990-0", NULL, }, },
112   { JISX0213_2000_1, { "jisx0213.2000-1", "jisx0208.1983-0", }, },
113   { JISX0213_2000_2, { "jisx0213.2000-2", NULL, }, },
114   { KSC5601_1987, { "ksc5601.1987-0", "ksx1001.1997-0", }, },
115 
116 #if 0
117   /*
118    * XXX
119    * UHC and JOHAB fonts are not used at the present time.
120    * see vt_vt100_parser.c:vt_parse_vt100_sequence().
121    */
122   { UHC, { NULL, NULL, }, },
123   { JOHAB, { "johabsh-1", /* "johabs-1" , */ "johab-1", }, },
124 #endif
125 
126   { GB2312_80, { "gb2312.1980-0", NULL, }, },
127   { GBK, { "gbk-0", NULL, }, },
128   { BIG5, { "big5.eten-0", "big5.hku-0", }, },
129   { HKSCS, { "big5hkscs-0", "big5-0", }, },
130   { CNS11643_1992_1, { "cns11643.1992-1", "cns11643.1992.1-0", }, },
131   { CNS11643_1992_2, { "cns11643.1992-2", "cns11643.1992.2-0", }, },
132   { CNS11643_1992_3, { "cns11643.1992-3", "cns11643.1992.3-0", }, },
133   { CNS11643_1992_4, { "cns11643.1992-4", "cns11643.1992.4-0", }, },
134   { CNS11643_1992_5, { "cns11643.1992-5", "cns11643.1992.5-0", }, },
135   { CNS11643_1992_6, { "cns11643.1992-6", "cns11643.1992.6-0", }, },
136   { CNS11643_1992_7, { "cns11643.1992-7", "cns11643.1992.7-0", }, },
137 };
138 
139 static int compose_dec_special_font;
140 static int use_point_size_for_fc;
141 static double dpi_for_fc;
142 
143 /* --- static functions --- */
144 
get_cs_info(ef_charset_t cs)145 static cs_info_t *get_cs_info(ef_charset_t cs) {
146   int count;
147 
148   for (count = 0; count < sizeof(cs_info_table) / sizeof(cs_info_t); count++) {
149     if (cs_info_table[count].cs == cs) {
150       return &cs_info_table[count];
151     }
152   }
153 
154 #ifdef DEBUG
155   bl_warn_printf(BL_DEBUG_TAG " not supported cs(%x).\n", cs);
156 #endif
157 
158   return NULL;
159 }
160 
161 #if !defined(NO_DYNAMIC_LOAD_TYPE)
xft_unset_font(ui_font_t * font)162 static void xft_unset_font(ui_font_t *font) {
163   void (*func)(ui_font_t *);
164 
165   if (!(func = ui_load_type_xft_func(UI_UNSET_FONT))) {
166     return;
167   }
168 
169   (*func)(font);
170 }
171 #elif defined(USE_TYPE_XFT)
172 void xft_unset_font(ui_font_t *font);
173 #endif
174 
175 #if !defined(NO_DYNAMIC_LOAD_TYPE)
cairo_unset_font(ui_font_t * font)176 static void cairo_unset_font(ui_font_t *font) {
177   void (*func)(ui_font_t *);
178 
179   if (!(func = ui_load_type_cairo_func(UI_UNSET_FONT))) {
180     return;
181   }
182 
183   (*func)(font);
184 }
185 #elif defined(USE_TYPE_CAIRO)
186 void cairo_unset_font(ui_font_t *font);
187 #endif
188 
set_decsp_font(ui_font_t * font)189 static int set_decsp_font(ui_font_t *font) {
190 /*
191  * freeing font->xfont or font->xft_font
192  */
193 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT)
194   if (font->xft_font) {
195     xft_unset_font(font);
196   }
197 #endif
198 
199 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_CAIRO)
200   if (font->cairo_font) {
201     cairo_unset_font(font);
202   }
203 #endif
204 
205 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
206   if (font->xfont) {
207     XFreeFont(font->display, font->xfont);
208     font->xfont = NULL;
209   }
210 #endif
211 
212   if ((font->decsp_font =
213            ui_decsp_font_new(font->display, font->width, font->height, font->ascent)) == NULL) {
214     return 0;
215   }
216 
217   /* decsp_font is impossible to draw double with. */
218   font->double_draw_gap = 0;
219 
220   /* decsp_font is always fixed pitch. */
221   font->is_proportional = 0;
222 
223   return 1;
224 }
225 
226 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
227 
xcore_calculate_char_width(Display * display,XFontStruct * xfont,u_int32_t ch)228 static u_int xcore_calculate_char_width(Display *display, XFontStruct *xfont, u_int32_t ch) {
229   int width;
230 
231   if (ch < 0x100) {
232     u_char c;
233 
234     c = ch;
235 
236     width = XTextWidth(xfont, &c, 1);
237   } else {
238     XChar2b c[2];
239 
240     width = XTextWidth16(xfont, c, ui_convert_ucs4_to_utf16(c, ch) / 2);
241   }
242 
243   if (width < 0) {
244     /* Some (indic) fonts could return minus value as text width. */
245     return 0;
246   } else {
247     return width;
248   }
249 }
250 
parse_xfont_name(char ** font_xlfd,char ** percent,char * font_name)251 static int parse_xfont_name(char **font_xlfd, char **percent, /* NULL can be returned. */
252                             char *font_name /* Don't specify NULL. Broken in this function */
253                             ) {
254   /*
255    * XFont format.
256    * [Font XLFD](:[Percentage])
257    */
258 
259   /* bl_str_sep() never returns NULL because font_name isn't NULL. */
260   *font_xlfd = bl_str_sep(&font_name, ":");
261 
262   /* may be NULL */
263   *percent = font_name;
264 
265   return 1;
266 }
267 
load_xfont_intern(Display * display,char * fontname,size_t max_len,const char * family,const char * weight,const char * slant,const char * width,const char * additional,u_int fontsize,const char * spacing,const char * encoding)268 static XFontStruct *load_xfont_intern(Display *display, char *fontname, size_t max_len,
269                                       const char *family, const char *weight,
270                                       const char *slant, const char *width, const char *additional,
271                                       u_int fontsize, const char *spacing, const char *encoding) {
272   bl_snprintf(fontname, max_len, "-*-%s-%s-%s-%s-%s-%d-*-*-*-%s-*-%s", family, weight, slant,
273               width, additional, fontsize, spacing, encoding);
274 
275 #ifdef DEBUG
276   bl_debug_printf(BL_DEBUG_TAG " loading %s.\n", fontname);
277 #endif
278 
279   return XLoadQueryFont(display, fontname);
280 }
281 
load_xfont(Display * display,const char * family,const char * weight,const char * slant,const char * width,u_int fontsize,const char * spacing,const char * encoding)282 static XFontStruct *load_xfont(Display *display, const char *family, const char *weight,
283                                const char *slant, const char *width, u_int fontsize,
284                                const char *spacing, const char *encoding) {
285   XFontStruct *xfont;
286   char *fontname;
287   size_t max_len;
288 
289   /* "+ 19" means the num of '-' , '*'(18byte) and null chars. */
290   max_len = strlen(family) + 7 /* unifont */ + strlen(weight) + strlen(slant) +
291             strlen(width) + 2 /* lang */ + DIGIT_STR_LEN(fontsize) + strlen(spacing) +
292             strlen(encoding) + 19;
293 
294   if ((fontname = alloca(max_len)) == NULL) {
295     return NULL;
296   }
297 
298   if ((xfont = load_xfont_intern(display, fontname, max_len, family, weight, slant, width, "*",
299                                  fontsize, spacing, encoding))) {
300     return xfont;
301   }
302 
303   if (strcmp(encoding, "iso10646-1") == 0 && strcmp(family, "biwidth") == 0) {
304     /* XFree86 Unicode font */
305 
306     if ((xfont = load_xfont_intern(display, fontname, max_len, "*", weight, slant, width,
307                                    bl_get_lang(), fontsize, spacing, encoding))) {
308       return xfont;
309     }
310 
311     if (strcmp(bl_get_lang(), "ja") != 0 &&
312         (xfont = load_xfont_intern(display, fontname, max_len, "*", weight, slant, width,
313                                    "ja", fontsize, spacing, encoding))) {
314       return xfont;
315     }
316 
317     /* GNU Unifont (-gnu-unifont) */
318 
319     return load_xfont_intern(display, fontname, max_len, "unifont", weight, slant,
320                              width, "*", fontsize, spacing, encoding);
321   }
322 
323   return NULL;
324 }
325 
xcore_set_font(ui_font_t * font,const char * fontname,u_int fontsize,u_int col_width,int use_medium_for_bold,int letter_space)326 static int xcore_set_font(ui_font_t *font, const char *fontname, u_int fontsize,
327                           u_int col_width, /* if usascii font wants to be set , 0 will be set */
328                           int use_medium_for_bold, int letter_space) {
329   XFontStruct *xfont;
330   u_int cols;
331   char *weight;
332   char *slant;
333   char *width;
334   char *family;
335   cs_info_t *csinfo;
336   char **font_encoding_p;
337   u_int percent;
338   int count;
339   int num_spacings;
340   char *spacings[] = {"c", "m", "p"};
341 
342   if ((csinfo = get_cs_info(FONT_CS(font->id))) == NULL) {
343 #ifdef DEBUG
344     bl_warn_printf(BL_DEBUG_TAG " get_cs_info(cs %x(id %x)) failed.\n", FONT_CS(font->id),
345                    font->id);
346 #endif
347 
348     return 0;
349   }
350 
351   if (fontname) {
352     char *p;
353     char *font_xlfd;
354     char *percent_str;
355 
356     if ((p = alloca(strlen(fontname) + 1)) == NULL) {
357       return 0;
358     }
359     strcpy(p, fontname);
360 
361     if (parse_xfont_name(&font_xlfd, &percent_str, p)) {
362 #ifdef DEBUG
363       bl_debug_printf(BL_DEBUG_TAG " loading %s font (%s percent).\n", font_xlfd, percent_str);
364 #endif
365 
366       while (1) {
367         if (!(xfont = XLoadQueryFont(font->display, font_xlfd))) {
368           char *xlfd;
369 
370           if ((xlfd = bl_str_replace(font_xlfd, "-bold-", "-medium-"))) {
371             xfont = XLoadQueryFont(font->display, xlfd);
372             free(xlfd);
373           }
374 
375           if (!xfont) {
376             bl_msg_printf("Font %s couldn't be loaded.\n", font_xlfd);
377 
378             break;
379           }
380 
381           font->double_draw_gap = 1;
382         } else {
383           font->double_draw_gap = use_medium_for_bold;
384         }
385 
386         if (percent_str == NULL || !bl_str_to_uint(&percent, percent_str)) {
387           percent = 0;
388         }
389 
390         goto font_found;
391       }
392     }
393   }
394 
395 /*
396  * searching apropriate font by using font info.
397  */
398 
399 #ifdef __DEBUG
400   bl_debug_printf("font for id %x will be loaded.\n", font->id);
401 #endif
402 
403   font->double_draw_gap = 0;
404 
405   percent = 0;
406 
407   if (font->id & FONT_BOLD) {
408     weight = "bold";
409   } else {
410     weight = "medium";
411   }
412 
413   if (font->id & FONT_ITALIC) {
414     slant = "i";
415   } else {
416     slant = "r";
417   }
418 
419   width = "normal";
420 
421   if ((font->id & FONT_FULLWIDTH) && (FONT_CS(font->id) == ISO10646_UCS4_1)) {
422     family = "biwidth";
423     num_spacings = sizeof(spacings) / sizeof(spacings[0]);
424   } else {
425     family = "fixed";
426     num_spacings = 1;
427   }
428 
429   for (count = 0; ; count++) {
430     FOREACH_FONT_ENCODINGS(csinfo, font_encoding_p) {
431       int idx;
432 
433       for (idx = 0; idx < num_spacings; idx++) {
434         if ((xfont = load_xfont(font->display, family, weight, slant, width, fontsize,
435                                 spacings[idx], *font_encoding_p))) {
436           goto font_found;
437         }
438       }
439     }
440 
441     if (count == 0) {
442       width = "*";
443       family = "*";
444       num_spacings = sizeof(spacings) / sizeof(spacings[0]);
445     } else if (count == 1) {
446       slant = "*";
447     } else if (count == 2) {
448       weight = "*";
449 
450       if (font->id & FONT_BOLD) {
451         /* no bold font is found. */
452         font->double_draw_gap = 1;
453       }
454     } else {
455       break;
456     }
457   }
458 
459   return 0;
460 
461 font_found:
462 
463   font->xfont = xfont;
464 
465   font->height = xfont->ascent + xfont->descent;
466   font->ascent = xfont->ascent;
467 
468   /*
469    * calculating actual font glyph width.
470    */
471   font->is_proportional = 0;
472   font->width = xfont->max_bounds.width;
473 
474   if (xfont->max_bounds.width != xfont->min_bounds.width) {
475     if (FONT_CS(font->id) == ISO10646_UCS4_1 || FONT_CS(font->id) == TIS620_2533) {
476       if (font->id & FONT_FULLWIDTH) {
477         /*
478          * XXX
479          * At the present time , all full width unicode fonts
480          * (which may include both half width and full width
481          * glyphs) are regarded as fixed.
482          * Since I don't know what chars to be compared to
483          * determine font proportion and width.
484          */
485       } else {
486         /*
487          * XXX
488          * A font including combining (0-width) glyphs or both half and
489          * full width glyphs.
490          * In this case , whether the font is proportional or not
491          * cannot be determined by comparing min_bounds and max_bounds,
492          * so if `i' and `W' chars have different width , the font is
493          * regarded as proportional (and `W' width is used as font->width).
494          */
495 
496         u_int w_width;
497         u_int i_width;
498 
499         if ((w_width = xcore_calculate_char_width(font->display, font->xfont, 'W')) == 0) {
500           font->is_proportional = 1;
501         } else if ((i_width = xcore_calculate_char_width(font->display, font->xfont, 'i')) == 0 ||
502                    w_width != i_width) {
503           font->is_proportional = 1;
504           font->width = w_width;
505         } else {
506           font->width = w_width;
507         }
508       }
509     } else {
510 #ifdef DEBUG
511       bl_warn_printf(BL_DEBUG_TAG " max font width(%d) and min one(%d) are mismatched.\n",
512                      xfont->max_bounds.width, xfont->min_bounds.width);
513 #endif
514 
515       font->is_proportional = 1;
516     }
517   }
518 
519   font->x_off = 0;
520 
521   if (font->id & FONT_FULLWIDTH) {
522     cols = 2;
523   } else {
524     cols = 1;
525   }
526 
527   if (col_width == 0) {
528     /* standard(usascii) font */
529 
530     if (!font->is_var_col_width) {
531       u_int ch_width;
532 
533       if (percent > 0) {
534         if (font->is_vertical) {
535           /* The width of full and half character font is the same. */
536           letter_space *= 2;
537           ch_width = DIVIDE_ROUNDING(fontsize * percent, 100);
538         } else {
539           ch_width = DIVIDE_ROUNDING(fontsize * percent, 200);
540         }
541       } else {
542         ch_width = font->width;
543 
544         if (font->is_vertical) {
545           /* The width of full and half character font is the same. */
546           letter_space *= 2;
547           ch_width *= 2;
548         }
549       }
550 
551       if (letter_space > 0 || ch_width > -letter_space) {
552         ch_width += letter_space;
553       }
554 
555       if (font->width != ch_width) {
556         font->is_proportional = 1;
557 
558         if (font->width < ch_width) {
559           /*
560            * If width(2) of '1' doesn't match ch_width(4)
561            * x_off = (4-2)/2 = 1.
562            * It means that starting position of drawing '1' is 1
563            * as follows.
564            *
565            *  0123
566            * +----+
567            * | ** |
568            * |  * |
569            * |  * |
570            * +----+
571            */
572           font->x_off = (ch_width - font->width) / 2;
573         }
574 
575         font->width = ch_width;
576       }
577     }
578   } else {
579     /* not a standard(usascii) font */
580 
581     /*
582      * XXX hack
583      * forcibly conforming non standard font width to standard font width.
584      */
585 
586     if (font->is_vertical) {
587       /*
588        * The width of full and half character font is the same.
589        * is_var_col_width is always false if is_vertical is true.
590        */
591       if (font->width != col_width) {
592         bl_msg_printf("Font(id %x) width(%d) doesn't fit column width(%d).\n",
593                       font->id, font->width, col_width);
594 
595         font->is_proportional = 1;
596 
597         if (font->width < col_width) {
598           font->x_off = (col_width - font->width) / 2;
599         }
600 
601         font->width = col_width;
602       }
603     } else if (font->width != col_width * cols) {
604       bl_msg_printf("Font(id %x) width(%d) doesn't fit column width(%d).\n",
605                     font->id, font->width, col_width * cols);
606 
607       font->is_proportional = 1;
608 
609       if (!font->is_var_col_width) {
610         if (font->width < col_width * cols) {
611           font->x_off = (col_width * cols - font->width) / 2;
612         }
613 
614         font->width = col_width * cols;
615       }
616     }
617   }
618 
619   /*
620    * checking if font width/height/ascent member is sane.
621    */
622 
623   if (font->width == 0) {
624 #ifdef DEBUG
625     bl_warn_printf(BL_DEBUG_TAG " font width is 0.\n");
626 #endif
627 
628     font->is_proportional = 1;
629 
630     /* XXX this may be inaccurate. */
631     font->width = DIVIDE_ROUNDINGUP(fontsize * cols, 2);
632   }
633 
634   if (font->height == 0) {
635     /* XXX this may be inaccurate. */
636     font->height = fontsize;
637   }
638 
639   if (font->ascent == 0) {
640     /* XXX this may be inaccurate. */
641     font->ascent = fontsize;
642   }
643 
644   /*
645    * set_decsp_font() is called after dummy font is loaded to get font metrics.
646    * Since dummy font encoding is "iso8859-1", loading rarely fails.
647    */
648   if (compose_dec_special_font && FONT_CS(font->id) == DEC_SPECIAL) {
649     return set_decsp_font(font);
650   }
651 
652   return 1;
653 }
654 
655 #endif
656 
657 #if !defined(NO_DYNAMIC_LOAD_TYPE)
658 
xft_calculate_char_width(ui_font_t * font,u_int32_t ch)659 static u_int xft_calculate_char_width(ui_font_t *font, u_int32_t ch /* US-ASCII or Unicode */
660                                       ) {
661   int (*func)(ui_font_t *, u_int32_t);
662 
663   if (!(func = ui_load_type_xft_func(UI_CALCULATE_CHAR_WIDTH))) {
664     return 0;
665   }
666 
667   return (*func)(font, ch);
668 }
669 
xft_set_font(ui_font_t * font,const char * fontname,u_int fontsize,u_int col_width,int letter_space,int aa_opt,int use_point_size,double dpi)670 static int xft_set_font(ui_font_t *font, const char *fontname, u_int fontsize,
671                         u_int col_width, /* if usascii font wants to be set , 0 will be set. */
672                         int letter_space,
673                         int aa_opt, /* 0 = default , 1 = enable , -1 = disable */
674                         int use_point_size, double dpi) {
675   int (*func)(ui_font_t *, const char *, u_int, u_int, u_int, int, int, double);
676 
677   if (!(func = ui_load_type_xft_func(UI_SET_FONT))) {
678     return 0;
679   }
680 
681   return (*func)(font, fontname, fontsize, col_width, letter_space, aa_opt, use_point_size,
682                  dpi);
683 }
684 
xft_set_ot_font(ui_font_t * font)685 static int xft_set_ot_font(ui_font_t *font) {
686   int (*func)(ui_font_t *);
687 
688   if (!(func = ui_load_type_xft_func(UI_SET_OT_FONT))) {
689     return 0;
690   }
691 
692   return (*func)(font);
693 }
694 
xft_convert_text_to_glyphs(ui_font_t * font,u_int32_t * shape_glyphs,u_int num_shape_glyphs,int8_t * xoffsets,int8_t * yoffsets,u_int8_t * advances,u_int32_t * noshape_glyphs,u_int32_t * src,u_int src_len,const char * script,const char * features)695 static u_int xft_convert_text_to_glyphs(ui_font_t *font, u_int32_t *shape_glyphs,
696                                         u_int num_shape_glyphs, int8_t *xoffsets,
697                                         int8_t *yoffsets, u_int8_t *advances,
698                                         u_int32_t *noshape_glyphs, u_int32_t *src, u_int src_len,
699                                         const char *script, const char *features) {
700   int (*func)(ui_font_t *, u_int32_t *, u_int, int8_t *, int8_t *, u_int8_t *, u_int32_t *,
701               u_int32_t *, u_int, const char *, const char *);
702 
703   if (!(func = ui_load_type_xft_func(UI_CONVERT_TEXT_TO_GLYPHS))) {
704     return 0;
705   }
706 
707   return (*func)(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets, advances,
708                  noshape_glyphs, src, src_len, script, features);
709 }
710 
711 #elif defined(USE_TYPE_XFT)
712 u_int xft_calculate_char_width(ui_font_t *font, u_int32_t ch);
713 int xft_set_font(ui_font_t *font, const char *fontname, u_int fontsize, u_int col_width,
714                  int letter_space, int aa_opt, int use_point_size, double dpi);
715 int xft_set_ot_font(ui_font_t *font);
716 #define xft_convert_text_to_glyphs(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets, \
717                                    advances, noshape_glyphs, src, src_len, script, features) \
718   ft_convert_text_to_glyphs(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets, advances, \
719                             noshape_glyphs, src, src_len, script, features)
720 u_int ft_convert_text_to_glyphs(ui_font_t *font, u_int32_t *shape_glyphs, int8_t *xoffsets,
721                                 int8_t *yoffsets, u_int8_t *advances, u_int num_shape_glyphs,
722                                 u_int32_t *noshape_glyphs, u_int32_t *src, u_int src_len,
723                                 const char *script, const char *features);
724 #endif
725 
726 #if !defined(NO_DYNAMIC_LOAD_TYPE)
727 
cairo_calculate_char_width(ui_font_t * font,u_int32_t ch)728 static u_int cairo_calculate_char_width(ui_font_t *font, u_int32_t ch /* US-ASCII or Unicode */
729                                         ) {
730   int (*func)(ui_font_t *, u_int32_t);
731 
732   if (!(func = ui_load_type_cairo_func(UI_CALCULATE_CHAR_WIDTH))) {
733     return 0;
734   }
735 
736   return (*func)(font, ch);
737 }
738 
cairo_set_font(ui_font_t * font,const char * fontname,u_int fontsize,u_int col_width,int letter_space,int aa_opt,int use_point_size,double dpi)739 static int cairo_set_font(ui_font_t *font, const char *fontname, u_int fontsize,
740                           u_int col_width, /* if usascii font wants to be set , 0 will be set. */
741                           int letter_space,
742                           int aa_opt, /* 0 = default , 1 = enable , -1 = disable */
743                           int use_point_size, double dpi) {
744   int (*func)(ui_font_t *, const char *, u_int, u_int, u_int, int, int, double);
745 
746   if (!(func = ui_load_type_cairo_func(UI_SET_FONT))) {
747     return 0;
748   }
749 
750   return (*func)(font, fontname, fontsize, col_width, letter_space, aa_opt, use_point_size, dpi);
751 }
752 
cairo_set_ot_font(ui_font_t * font)753 static int cairo_set_ot_font(ui_font_t *font) {
754   int (*func)(ui_font_t *);
755 
756   if (!(func = ui_load_type_cairo_func(UI_SET_OT_FONT))) {
757     return 0;
758   }
759 
760   return (*func)(font);
761 }
762 
cairo_convert_text_to_glyphs(ui_font_t * font,u_int32_t * shape_glyphs,u_int num_shape_glyphs,int8_t * xoffsets,int8_t * yoffsets,u_int8_t * advances,u_int32_t * noshape_glyphs,u_int32_t * src,u_int src_len,const char * script,const char * features)763 static u_int cairo_convert_text_to_glyphs(ui_font_t *font, u_int32_t *shape_glyphs,
764                                           u_int num_shape_glyphs, int8_t *xoffsets,
765                                           int8_t *yoffsets, u_int8_t *advances,
766                                           u_int32_t *noshape_glyphs, u_int32_t *src, u_int src_len,
767                                           const char *script, const char *features) {
768   int (*func)(ui_font_t *, u_int32_t *, u_int, int8_t *, int8_t *, u_int8_t *, u_int32_t *,
769               u_int32_t *, u_int, const char *, const char *);
770 
771   if (!(func = ui_load_type_cairo_func(UI_CONVERT_TEXT_TO_GLYPHS))) {
772     return 0;
773   }
774 
775   return (*func)(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets, advances,
776                  noshape_glyphs, src, src_len, script, features);
777 }
778 
779 #elif defined(USE_TYPE_CAIRO)
780 u_int cairo_calculate_char_width(ui_font_t *font, u_int32_t ch);
781 int cairo_set_font(ui_font_t *font, const char *fontname, u_int fontsize, u_int col_width,
782                    int letter_space, int aa_opt, int use_point_size, double dpi);
783 int cairo_set_ot_font(ui_font_t *font);
784 #define cairo_convert_text_to_glyphs(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets, \
785                                      advances, noshape_glyphs, src, src_len, script, features) \
786   ft_convert_text_to_glyphs(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets, advances, \
787                             noshape_glyphs, src, src_len, script, features)
788 #ifndef USE_TYPE_XFT
789 u_int ft_convert_text_to_glyphs(ui_font_t *font, u_int32_t *shape_glyphs, int8_t *xoffsets,
790                                 int8_t *yoffsets, u_int8_t *advances, u_int num_shape_glyphs,
791                                 u_int32_t *noshape_glyphs, u_int32_t *src, u_int src_len,
792                                 const char *script, const char *features);
793 #endif
794 #endif
795 
calculate_char_width(ui_font_t * font,u_int32_t ch,ef_charset_t cs)796 static u_int calculate_char_width(ui_font_t *font, u_int32_t ch, ef_charset_t cs) {
797 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT)
798   if (font->xft_font) {
799     if (cs != US_ASCII && cs != ISO8859_1_R && !IS_ISCII(cs)) {
800       if (!(ch = ui_convert_to_xft_ucs4(ch, cs))) {
801         return 0;
802       }
803     }
804 
805     return xft_calculate_char_width(font, ch);
806   }
807 #endif
808 
809 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_CAIRO)
810   if (font->cairo_font) {
811     if (cs != US_ASCII && cs != ISO8859_1_R && !IS_ISCII(cs)) {
812       if (!(ch = ui_convert_to_xft_ucs4(ch, cs))) {
813         return 0;
814       }
815     }
816 
817     return cairo_calculate_char_width(font, ch);
818   }
819 #endif
820 
821 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
822   if (font->xfont) {
823     return xcore_calculate_char_width(font->display, font->xfont, ch);
824   }
825 #endif
826 
827 #ifdef DEBUG
828   bl_debug_printf(BL_DEBUG_TAG " couldn't calculate correct font width.\n");
829 #endif
830 
831   return 0;
832 }
833 
834 /* --- global functions --- */
835 
ui_compose_dec_special_font(void)836 void ui_compose_dec_special_font(void) {
837   compose_dec_special_font = 1;
838 }
839 
ui_font_new(Display * display,vt_font_t id,int size_attr,ui_type_engine_t type_engine,ui_font_present_t font_present,const char * fontname,u_int fontsize,u_int col_width,int use_medium_for_bold,int letter_space)840 ui_font_t *ui_font_new(Display *display, vt_font_t id, int size_attr, ui_type_engine_t type_engine,
841                        /* FONT_VAR_WIDTH is never set if FONT_VERTICAL is set. */
842                        ui_font_present_t font_present,
843                        const char *fontname, u_int fontsize, u_int col_width,
844                        int use_medium_for_bold,
845                        int letter_space /* ignored in variable column width mode. */) {
846   ui_font_t *font;
847 
848   if ((font = calloc(1, sizeof(ui_font_t))) == NULL) {
849 #ifdef DEBUG
850     bl_warn_printf(BL_DEBUG_TAG " malloc() failed.\n");
851 #endif
852 
853     return NULL;
854   }
855 
856   font->display = display;
857   font->id = id;
858 
859   if (font_present & FONT_VAR_WIDTH) {
860     font->is_var_col_width = 1;
861   } else if (IS_ISCII(FONT_CS(font->id))) {
862     /*
863      * For exampe, 'W' width and 'l' width of OR-TTSarala font for ISCII_ORIYA
864      * are the same by chance, though it is actually a proportional font.
865      */
866     font->is_var_col_width = font->is_proportional = 1;
867   }
868 
869   if (font_present & FONT_VERTICAL) {
870     font->is_vertical = 1;
871   }
872 
873   if (size_attr >= DOUBLE_HEIGHT_TOP) {
874     fontsize *= 2;
875     col_width *= 2;
876   }
877 
878   switch (type_engine) {
879     default:
880       return NULL;
881 
882 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT)
883     case TYPE_XFT:
884       if (!xft_set_font(font, fontname, fontsize, col_width, letter_space,
885                         (font_present & FONT_AA) == FONT_AA
886                             ? 1
887                             : ((font_present & FONT_NOAA) == FONT_NOAA ? -1 : 0),
888                         use_point_size_for_fc, dpi_for_fc)) {
889         free(font);
890 
891         return NULL;
892       }
893 
894       break;
895 #endif
896 
897 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_CAIRO)
898     case TYPE_CAIRO:
899       if (!cairo_set_font(font, fontname, fontsize, col_width, letter_space,
900                           (font_present & FONT_AA) == FONT_AA
901                               ? 1
902                               : ((font_present & FONT_NOAA) == FONT_NOAA ? -1 : 0),
903                           use_point_size_for_fc, dpi_for_fc)) {
904         free(font);
905 
906         return NULL;
907       }
908 
909       break;
910 #endif
911 
912 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
913     case TYPE_XCORE:
914       if (font_present & FONT_AA) {
915         return NULL;
916       } else if (!xcore_set_font(font, fontname, fontsize, col_width, use_medium_for_bold,
917                                  letter_space)) {
918         if (size_attr >= DOUBLE_HEIGHT_TOP &&
919             xcore_set_font(font, fontname, fontsize / 2, col_width, use_medium_for_bold,
920                            letter_space)) {
921           goto end;
922         }
923 
924         free(font);
925 
926         return NULL;
927       }
928 
929       goto end;
930 #endif
931   }
932 
933   /*
934    * set_decsp_font() is called after dummy xft/cairo font is loaded to get font
935    * metrics.
936    * Since dummy font encoding is "iso8859-1", loading rarely fails.
937    */
938   /* XXX dec specials must always be composed for now */
939   if (/* compose_dec_special_font && */ FONT_CS(font->id) == DEC_SPECIAL) {
940     set_decsp_font(font);
941   }
942 
943 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
944 end:
945 #endif
946   if (size_attr == DOUBLE_WIDTH) {
947     if (type_engine == TYPE_CAIRO) {
948       font->x_off *= 2;
949     } else {
950       font->x_off += (font->width / 2);
951       font->is_proportional = 1;
952       font->is_var_col_width = 0;
953     }
954     font->width *= 2;
955   }
956 
957   font->size_attr = size_attr;
958 
959   if (font->is_proportional && !font->is_var_col_width) {
960     bl_msg_printf("Font(id %x): Draw one char at a time to fit column width.\n", font->id);
961   }
962 
963 #ifdef DEBUG
964   ui_font_dump(font);
965 #endif
966 
967   return font;
968 }
969 
ui_font_destroy(ui_font_t * font)970 void ui_font_destroy(ui_font_t *font) {
971 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT)
972   if (font->xft_font) {
973     xft_unset_font(font);
974   }
975 #endif
976 
977 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_CAIRO)
978   if (font->cairo_font) {
979     cairo_unset_font(font);
980   }
981 #endif
982 
983 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
984   if (font->xfont) {
985     XFreeFont(font->display, font->xfont);
986     font->xfont = NULL;
987   }
988 #endif
989 
990   if (font->decsp_font) {
991     ui_decsp_font_destroy(font->decsp_font, font->display);
992     font->decsp_font = NULL;
993   }
994 
995   free(font);
996 }
997 
ui_calculate_char_width(ui_font_t * font,u_int32_t ch,ef_charset_t cs,int is_awidth,int * draw_alone)998 u_int ui_calculate_char_width(ui_font_t *font, u_int32_t ch, ef_charset_t cs, int is_awidth,
999                               int *draw_alone) {
1000   if (draw_alone) {
1001     *draw_alone = 0;
1002   }
1003 
1004   if (font->is_proportional) {
1005     if (font->is_var_col_width) {
1006       /* Returned value can be 0 if iscii font is used. */
1007       return calculate_char_width(font, ch, cs);
1008     }
1009 
1010     if (draw_alone) {
1011       *draw_alone = 1;
1012     }
1013   } else if (draw_alone &&
1014              cs == ISO10646_UCS4_1 /* ISO10646_UCS4_1_V is always proportional */) {
1015     if (is_awidth ||
1016         /*
1017          * The width of U+2590 and U+2591 is narrow in EastAsianWidth-6.3.0
1018          * but the glyphs in GNU Unifont are full-width unexpectedly.
1019          */
1020         ch == 0x2590 || ch == 0x2591) {
1021       if (calculate_char_width(font, ch, cs) != font->width) {
1022         *draw_alone = 1;
1023       }
1024     }
1025   }
1026 
1027   return font->width;
1028 }
1029 
ui_font_get_encoding_names(ef_charset_t cs)1030 char **ui_font_get_encoding_names(ef_charset_t cs) {
1031   cs_info_t *info;
1032 
1033   if ((info = get_cs_info(cs))) {
1034     return info->encoding_names;
1035   } else {
1036     return NULL;
1037   }
1038 }
1039 
ui_font_use_point_size(int use)1040 void ui_font_use_point_size(int use) { use_point_size_for_fc = use; }
1041 
ui_font_has_ot_layout_table(ui_font_t * font)1042 int ui_font_has_ot_layout_table(ui_font_t *font) {
1043 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_CAIRO)
1044   if (font->cairo_font) {
1045 #ifdef USE_OT_LAYOUT
1046     if (!font->ot_font) {
1047       if (font->ot_font_not_found) {
1048         return 0;
1049       }
1050 
1051       if (!cairo_set_ot_font(font)) {
1052         font->ot_font_not_found = 1;
1053 
1054         return 0;
1055       }
1056     }
1057 #endif
1058 
1059     return 1;
1060   }
1061 #endif
1062 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT)
1063   if (font->xft_font) {
1064 #ifdef USE_OT_LAYOUT
1065     if (!font->ot_font) {
1066       if (font->ot_font_not_found) {
1067         return 0;
1068       }
1069 
1070       if (!xft_set_ot_font(font)) {
1071         font->ot_font_not_found = 1;
1072 
1073         return 0;
1074       }
1075     }
1076 #endif
1077 
1078     return 1;
1079   }
1080 #endif
1081 
1082   return 0;
1083 }
1084 
ui_convert_text_to_glyphs(ui_font_t * font,u_int32_t * shape_glyphs,u_int num_shape_glyphs,int8_t * xoffsets,int8_t * yoffsets,u_int8_t * advances,u_int32_t * noshape_glyphs,u_int32_t * src,u_int src_len,const char * script,const char * features)1085 u_int ui_convert_text_to_glyphs(ui_font_t *font, /* always has ot_font */
1086                                 u_int32_t *shape_glyphs, u_int num_shape_glyphs, int8_t *xoffsets,
1087                                 int8_t *yoffsets, u_int8_t *advances, u_int32_t *noshape_glyphs,
1088                                 u_int32_t *src, u_int src_len,
1089                                 const char *script, const char *features) {
1090 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_CAIRO)
1091   if (font->cairo_font) {
1092     return cairo_convert_text_to_glyphs(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets,
1093                                         advances, noshape_glyphs, src, src_len, script, features);
1094   }
1095 #endif
1096 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT)
1097   if (font->xft_font) {
1098     return xft_convert_text_to_glyphs(font, shape_glyphs, num_shape_glyphs, xoffsets, yoffsets,
1099                                       advances, noshape_glyphs, src, src_len, script, features);
1100   }
1101 #endif
1102 
1103   return 0;
1104 }
1105 
1106 /* For mlterm-libvte */
ui_font_set_dpi_for_fc(double dpi)1107 void ui_font_set_dpi_for_fc(double dpi) { dpi_for_fc = dpi; }
1108 
1109 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT) || defined(USE_TYPE_CAIRO)
1110 
1111 static int use_cp932_ucs_for_xft = 0;
1112 
ui_use_cp932_ucs_for_xft(void)1113 void ui_use_cp932_ucs_for_xft(void) {
1114   use_cp932_ucs_for_xft = 1;
1115 }
1116 
1117 /*
1118  * used only for xft or cairo.
1119  * Don't call this function for US_ASCII, ISO8859_1_R and ISCII.
1120  */
ui_convert_to_xft_ucs4(u_int32_t ch,ef_charset_t cs)1121 u_int32_t ui_convert_to_xft_ucs4(u_int32_t ch, ef_charset_t cs) {
1122   ef_char_t non_ucs;
1123   ef_char_t ucs4;
1124 
1125   if (IS_ISO10646_UCS4(cs) /* || cs == ISO10646_UCS2_1 */) {
1126     return ch;
1127   } else if (use_cp932_ucs_for_xft && cs == JISX0208_1983) {
1128     if (ch == 0x2140) {
1129       return 0xff3c;
1130     } else if (ch == 0x2141) {
1131       return 0xff5e;
1132     } else if (ch == 0x2142) {
1133       return 0x2225;
1134     } else if (ch == 0x215d) {
1135       return 0xff0d;
1136     } else if (ch == 0x2171) {
1137       return 0xffe0;
1138     } else if (ch == 0x2172) {
1139       return 0xffe1;
1140     } else if (ch == 0x224c) {
1141       return 0xffe2;
1142     }
1143   }
1144 
1145   non_ucs.size = CS_SIZE(cs);
1146   non_ucs.property = 0;
1147   non_ucs.cs = cs;
1148   ef_int_to_bytes(non_ucs.ch, non_ucs.size, ch);
1149 
1150   if (vt_is_msb_set(cs)) {
1151     u_int count;
1152 
1153     for (count = 0; count < non_ucs.size; count++) {
1154       non_ucs.ch[count] &= 0x7f;
1155     }
1156   }
1157 
1158   if (ef_map_to_ucs4(&ucs4, &non_ucs)) {
1159     return ef_char_to_int(&ucs4);
1160   } else {
1161     return 0;
1162   }
1163 }
1164 
1165 #endif
1166 
1167 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
1168 
1169 /* Return written size */
ui_convert_ucs4_to_utf16(u_char * dst,u_int32_t src)1170 size_t ui_convert_ucs4_to_utf16(u_char *dst, /* 4 bytes. Big endian. */
1171                                 u_int32_t src) {
1172 #if 0
1173   bl_debug_printf(BL_DEBUG_TAG "%.8x => ", src);
1174 #endif
1175 
1176   if (src < 0x10000) {
1177     dst[0] = (src >> 8) & 0xff;
1178     dst[1] = src & 0xff;
1179 
1180     return 2;
1181   } else if (src < 0x110000) {
1182     /* surrogate pair */
1183 
1184     u_char c;
1185 
1186     src -= 0x10000;
1187     c = (u_char)(src / (0x100 * 0x400));
1188     src -= (c * 0x100 * 0x400);
1189     dst[0] = c + 0xd8;
1190 
1191     c = (u_char)(src / 0x400);
1192     src -= (c * 0x400);
1193     dst[1] = c;
1194 
1195     c = (u_char)(src / 0x100);
1196     src -= (c * 0x100);
1197     dst[2] = c + 0xdc;
1198     dst[3] = (u_char)src;
1199 
1200 #if 0
1201     bl_msg_printf("%.2x%.2x%.2x%.2x\n", dst[0], dst[1], dst[2], dst[3]);
1202 #endif
1203 
1204     return 4;
1205   }
1206 
1207   return 0;
1208 }
1209 
1210 #endif
1211 
1212 #ifdef DEBUG
1213 
ui_font_dump(ui_font_t * font)1214 void ui_font_dump(ui_font_t *font) {
1215 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XCORE)
1216   bl_msg_printf("Font id %x: XFont %p ", font->id, font->xfont);
1217 #endif
1218 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_XFT)
1219   bl_msg_printf("Font id %x: XftFont %p ", font->id, font->xft_font);
1220 #endif
1221 #if !defined(NO_DYNAMIC_LOAD_TYPE) || defined(USE_TYPE_CAIRO)
1222   bl_msg_printf("Font id %x: CairoFont %p ", font->id, font->cairo_font);
1223 #endif
1224 
1225   bl_msg_printf("(width %d, height %d, ascent %d, x_off %d, gap %d)", font->width, font->height,
1226                 font->ascent, font->x_off, font->double_draw_gap);
1227 
1228   if (font->is_proportional) {
1229     bl_msg_printf(" (proportional)");
1230   }
1231 
1232   if (font->is_var_col_width) {
1233     bl_msg_printf(" (var col width)");
1234   }
1235 
1236   if (font->is_vertical) {
1237     bl_msg_printf(" (vertical)");
1238   }
1239 
1240   if (font->double_draw_gap) {
1241     bl_msg_printf(" (double drawing)");
1242   }
1243 
1244   bl_msg_printf("\n");
1245 }
1246 
1247 #endif
1248