1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include "../ui_font.h"
4 
5 #include <stdio.h>
6 #include <string.h> /* memset/strncasecmp */
7 #include <pobl/bl_debug.h>
8 #include <pobl/bl_str.h>    /* bl_snprintf */
9 #include <pobl/bl_mem.h>    /* alloca */
10 #include <pobl/bl_str.h>    /* bl_str_sep/bl_str_to_int */
11 #include <pobl/bl_locale.h> /* bl_get_lang() */
12 #include <mef/ef_ucs4_map.h>
13 #include <vt_char_encoding.h>
14 
15 #ifdef USE_OT_LAYOUT
16 #include "otl.h"
17 #endif
18 
19 #define FOREACH_FONT_ENCODINGS(csinfo, font_encoding_p) \
20   for ((font_encoding_p) = &csinfo->encoding_names[0]; *(font_encoding_p); (font_encoding_p)++)
21 
22 #if 0
23 #define __DEBUG
24 #endif
25 
26 typedef struct wincs_info {
27   DWORD cs;
28   vt_char_encoding_t encoding;
29 
30 } wincs_info_t;
31 
32 typedef struct cs_info {
33   ef_charset_t cs;
34   DWORD wincs;
35 
36 } cs_info_t;
37 
38 /* --- static variables --- */
39 
40 static wincs_info_t wincs_info_table[] = {
41   { DEFAULT_CHARSET, VT_UNKNOWN_ENCODING, },
42   { SYMBOL_CHARSET, VT_UNKNOWN_ENCODING, },
43   { OEM_CHARSET, VT_UNKNOWN_ENCODING, },
44   { ANSI_CHARSET, VT_CP1252, },
45   { RUSSIAN_CHARSET, VT_CP1251, },
46   { GREEK_CHARSET, VT_CP1253, },
47   { TURKISH_CHARSET, VT_CP1254, },
48   { BALTIC_CHARSET, VT_CP1257, },
49   { HEBREW_CHARSET, VT_CP1255, },
50   { ARABIC_CHARSET, VT_CP1256, },
51   { SHIFTJIS_CHARSET, VT_SJIS, },
52   { HANGEUL_CHARSET, VT_UHC, },
53   { GB2312_CHARSET, VT_GBK, },
54   { CHINESEBIG5_CHARSET, VT_BIG5},
55   { JOHAB_CHARSET, VT_JOHAB, },
56   { THAI_CHARSET, VT_TIS620, },
57   { EASTEUROPE_CHARSET, VT_ISO8859_3, },
58   { MAC_CHARSET, VT_UNKNOWN_ENCODING, },
59 };
60 
61 static cs_info_t cs_info_table[] = {
62   { ISO10646_UCS4_1, DEFAULT_CHARSET, },
63   { ISO10646_UCS4_1_V, DEFAULT_CHARSET, },
64 
65   { DEC_SPECIAL, SYMBOL_CHARSET, },
66   { ISO8859_1_R, ANSI_CHARSET, },
67   { ISO8859_2_R, DEFAULT_CHARSET, },
68   { ISO8859_3_R, EASTEUROPE_CHARSET, },
69   { ISO8859_4_R, DEFAULT_CHARSET, },
70   { ISO8859_5_R, RUSSIAN_CHARSET, },
71   { ISO8859_6_R, ARABIC_CHARSET, },
72   { ISO8859_7_R, GREEK_CHARSET, },
73   { ISO8859_8_R, HEBREW_CHARSET, },
74   { ISO8859_9_R, TURKISH_CHARSET, },
75   { ISO8859_10_R, DEFAULT_CHARSET, },
76   { TIS620_2533, THAI_CHARSET, },
77   { ISO8859_13_R, DEFAULT_CHARSET, },
78   { ISO8859_14_R, DEFAULT_CHARSET, },
79   { ISO8859_15_R, DEFAULT_CHARSET, },
80   { ISO8859_16_R, DEFAULT_CHARSET, },
81   { TCVN5712_3_1993, VIETNAMESE_CHARSET, },
82   { ISCII_ASSAMESE, DEFAULT_CHARSET, },
83   { ISCII_BENGALI, DEFAULT_CHARSET, },
84   { ISCII_GUJARATI, DEFAULT_CHARSET, },
85   { ISCII_HINDI, DEFAULT_CHARSET, },
86   { ISCII_KANNADA, DEFAULT_CHARSET, },
87   { ISCII_MALAYALAM, DEFAULT_CHARSET, },
88   { ISCII_ORIYA, DEFAULT_CHARSET, },
89   { ISCII_PUNJABI, DEFAULT_CHARSET, },
90   { ISCII_TAMIL, DEFAULT_CHARSET, },
91   { ISCII_TELUGU, DEFAULT_CHARSET, },
92   { VISCII, VIETNAMESE_CHARSET, },
93   { KOI8_R, RUSSIAN_CHARSET, },
94   { KOI8_U, RUSSIAN_CHARSET, },
95   { KOI8_T, RUSSIAN_CHARSET, },
96   { GEORGIAN_PS, DEFAULT_CHARSET, },
97   { CP1250, EASTEUROPE_CHARSET, },
98   { CP1251, RUSSIAN_CHARSET, },
99   { CP1252, ANSI_CHARSET, },
100   { CP1253, GREEK_CHARSET, },
101   { CP1254, TURKISH_CHARSET, },
102   { CP1255, HEBREW_CHARSET, },
103   { CP1256, ARABIC_CHARSET, },
104   { CP1257, BALTIC_CHARSET, },
105   { CP1258, VIETNAMESE_CHARSET, },
106 
107   { JISX0201_KATA, SHIFTJIS_CHARSET, },
108   { JISX0201_ROMAN, SHIFTJIS_CHARSET, },
109   { JISC6226_1978, SHIFTJIS_CHARSET, },
110   { JISX0208_1983, SHIFTJIS_CHARSET, },
111   { JISX0208_1990, SHIFTJIS_CHARSET, },
112   { JISX0212_1990, SHIFTJIS_CHARSET, },
113   { JISX0213_2000_1, SHIFTJIS_CHARSET, },
114   { JISX0213_2000_2, SHIFTJIS_CHARSET, },
115 
116   { KSC5601_1987, HANGEUL_CHARSET, },
117   { UHC, HANGEUL_CHARSET, },
118   { JOHAB, JOHAB_CHARSET, },
119 
120   { GB2312_80, GB2312_CHARSET, },
121   { GBK, GB2312_CHARSET, },
122   { BIG5, CHINESEBIG5_CHARSET, },
123   { HKSCS, DEFAULT_CHARSET, },
124   { CNS11643_1992_1, GB2312_CHARSET, },
125   { CNS11643_1992_2, GB2312_CHARSET, },
126   { CNS11643_1992_3, GB2312_CHARSET, },
127   { CNS11643_1992_4, GB2312_CHARSET, },
128   { CNS11643_1992_5, GB2312_CHARSET, },
129   { CNS11643_1992_6, GB2312_CHARSET, },
130   { CNS11643_1992_7, GB2312_CHARSET, },
131 };
132 
133 static GC display_gc;
134 
135 static int use_point_size;
136 
137 /* --- static functions --- */
138 
get_wincs_info(ef_charset_t cs)139 static wincs_info_t *get_wincs_info(ef_charset_t cs) {
140   int count;
141 
142   for (count = 0; count < sizeof(cs_info_table) / sizeof(cs_info_t); count++) {
143     if (cs_info_table[count].cs == cs) {
144       DWORD wincs;
145 
146       wincs = cs_info_table[count].wincs;
147 
148       for (count = 0; count < sizeof(wincs_info_table) / sizeof(wincs_info_t); count++) {
149         if (wincs_info_table[count].cs == wincs) {
150           return &wincs_info_table[count];
151         }
152       }
153 
154       break;
155     }
156   }
157 
158 #ifdef DEBUG
159   bl_warn_printf(BL_DEBUG_TAG " not supported cs(%x).\n", cs);
160 #endif
161 
162   return NULL;
163 }
164 
parse_font_name(char ** font_family,int * font_weight,int * is_italic,double * font_size,u_int * percent,char * font_name)165 static int parse_font_name(
166     char **font_family, int *font_weight, /* if weight is not specified in
167                                              font_name , not changed. */
168     int *is_italic,    /* if slant is not specified in font_name , not changed. */
169     double *font_size, /* if size is not specified in font_name , not changed. */
170     u_int *percent,    /* if percent is not specified in font_name , not changed. */
171     char *font_name    /* modified by this function. */
172     ) {
173   char *p;
174   size_t len;
175 
176   /*
177    * Format.
178    * [Family]( [WEIGHT] [SLANT] [SIZE]:[Percentage])
179    */
180 
181   *font_family = font_name;
182 
183   if ((p = strrchr(font_name, ':'))) {
184     /* Parsing ":[Percentage]" */
185     if (bl_str_to_uint(percent, p + 1)) {
186       *p = '\0';
187     }
188 #ifdef DEBUG
189     else {
190       bl_warn_printf(BL_DEBUG_TAG " Percentage(%s) is illegal.\n", p + 1);
191     }
192 #endif
193   }
194 
195 /*
196  * Parsing "[Family] [WEIGHT] [SLANT] [SIZE]".
197  * Following is the same as libtype/ui_font_ft.c and fb/ui_font.c.
198  * except FW_* and is_italic.
199  */
200 
201 #if 0
202   bl_debug_printf("Parsing %s for [Family] [Weight] [Slant]\n", *font_family);
203 #endif
204 
205   p = bl_str_chop_spaces(*font_family);
206   len = strlen(p);
207   while (len > 0) {
208     size_t step = 0;
209 
210     if (*p == ' ') {
211       char *orig_p;
212 
213       orig_p = p;
214       do {
215         p++;
216         len--;
217       } while (*p == ' ');
218 
219       if (len == 0) {
220         *orig_p = '\0';
221 
222         break;
223       } else {
224         int count;
225         struct {
226           char *style;
227           int weight;
228           int is_italic;
229 
230         } styles[] = {
231           /*
232            * Portable styles.
233            */
234           /* slant */
235           { "italic", 0, 1, },
236           /* weight */
237           { "bold", FW_BOLD, 0, },
238 
239           /*
240            * Hack for styles which can be returned by
241            * gtk_font_selection_dialog_get_font_name().
242            */
243           /* slant */
244           { "oblique", /* XXX This style is ignored. */ 0, 0, },
245           /* weight */
246           { "light", /* e.g. "Bookman Old Style Light" */ FW_LIGHT, 0, },
247           { "semi-bold", FW_SEMIBOLD, 0, },
248           { "heavy", /* e.g. "Arial Black Heavy" */ FW_HEAVY, 0, },
249           /* other */
250           { "semi-condensed", /* XXX This style is ignored. */ 0, 0, },
251         };
252 
253         for (count = 0; count < sizeof(styles) / sizeof(styles[0]); count++) {
254           size_t len_v;
255 
256           len_v = strlen(styles[count].style);
257 
258           /* XXX strncasecmp is not portable? */
259           if (len >= len_v && strncasecmp(p, styles[count].style, len_v) == 0) {
260             *orig_p = '\0';
261             step = len_v;
262             if (styles[count].weight) {
263               *font_weight = styles[count].weight;
264             } else if (styles[count].is_italic) {
265               *is_italic = 1;
266             }
267 
268             goto next_char;
269           }
270         }
271 
272         if (*p != '0' ||      /* In case of "DevLys 010" font family. */
273             *(p + 1) == '\0') /* "MS Gothic 0" => "MS Gothic" + "0" */
274         {
275           char *end;
276           double size;
277 
278           size = strtod(p, &end);
279           if (*end == '\0') {
280             /* p has no more parameters. */
281 
282             *orig_p = '\0';
283             if (size > 0) {
284               *font_size = size;
285             }
286 
287             break;
288           }
289         }
290 
291         step = 1;
292       }
293     } else {
294       step = 1;
295     }
296 
297   next_char:
298     p += step;
299     len -= step;
300   }
301 
302   return 1;
303 }
304 
calculate_char_width(ui_font_t * font,u_int32_t ch,ef_charset_t cs)305 static u_int calculate_char_width(ui_font_t *font, u_int32_t ch, ef_charset_t cs) {
306   SIZE sz;
307 
308   if (!display_gc) {
309     /*
310      * Cached as far as ui_caculate_char_width is called.
311      * display_gc is destroyed in ui_font_new or ui_font_destroy.
312      */
313     display_gc = CreateIC("Display", NULL, NULL, NULL);
314   }
315 
316   SelectObject(display_gc, font->xfont->fid);
317 
318   if (cs != US_ASCII && !IS_ISCII(cs)) {
319     u_int32_t ucs4_code;
320     u_char utf16[4];
321     int len;
322     BOOL ret;
323 
324     if (IS_ISO10646_UCS4(cs)) {
325       ucs4_code = ch;
326     } else {
327       ef_char_t non_ucs;
328       ef_char_t ucs4;
329 
330       non_ucs.size = CS_SIZE(cs);
331       non_ucs.property = 0;
332       non_ucs.cs = cs;
333       ef_int_to_bytes(non_ucs.ch, non_ucs.size, ch);
334 
335       if (vt_is_msb_set(cs)) {
336         u_int count;
337 
338         for (count = 0; count < non_ucs.size; count++) {
339           non_ucs.ch[count] &= 0x7f;
340         }
341       }
342 
343       if (ef_map_to_ucs4(&ucs4, &non_ucs)) {
344         ucs4_code = ef_bytes_to_int(ucs4.ch, 4);
345       } else {
346         return 0;
347       }
348     }
349 
350     len = ui_convert_ucs4_to_utf16(utf16, ucs4_code) / 2;
351 
352 #ifdef USE_OT_LAYOUT
353     if (font->use_ot_layout /* && font->ot_font */) {
354 #ifdef USE_WIN32API
355       /* GetTextExtentPointI doesn't exist on mingw-i686-pc-mingw */
356       static BOOL (*func)(HDC, LPWORD, int, LPSIZE);
357 
358       if (!func) {
359         func = GetProcAddress(GetModuleHandleA("GDI32.DLL"), "GetTextExtentPointI");
360       }
361 
362       ret = (*func)(display_gc, utf16, len, &sz);
363 #else
364       ret = GetTextExtentPointI(display_gc, utf16, len, &sz);
365 #endif
366     } else
367 #endif
368     {
369       ret = GetTextExtentPoint32W(display_gc, utf16, len, &sz);
370     }
371 
372     if (!ret) {
373       return 0;
374     }
375   } else {
376     u_char c;
377 
378     c = ch;
379     if (!GetTextExtentPoint32A(display_gc, &c, 1, &sz)) {
380       return 0;
381     }
382   }
383 
384   if (sz.cx < 0) {
385     return 0;
386   } else {
387     return sz.cx;
388   }
389 }
390 
391 /* --- global functions --- */
392 
ui_compose_dec_special_font(void)393 void ui_compose_dec_special_font(void) {
394   /* Do nothing for now in win32. */
395 }
396 
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)397 ui_font_t *ui_font_new(Display *display, vt_font_t id, int size_attr, ui_type_engine_t type_engine,
398                        ui_font_present_t font_present, const char *fontname, u_int fontsize,
399                        u_int col_width, int use_medium_for_bold, int letter_space) {
400   ui_font_t *font;
401   u_int cols;
402   wincs_info_t *wincsinfo;
403   char *font_family;
404   wchar_t *w_font_family;
405   int wlen;
406   int weight;
407   int is_italic;
408   int width;
409   int height;
410   double fontsize_d;
411   u_int percent;
412 
413   if (type_engine != TYPE_XCORE ||
414       (font = calloc(1, sizeof(ui_font_t) + sizeof(XFontStruct))) == NULL) {
415 #ifdef DEBUG
416     bl_warn_printf(BL_DEBUG_TAG " malloc() failed.\n");
417 #endif
418 
419     return NULL;
420   }
421 
422   font->xfont = font + 1;
423 
424   font->display = display;
425   font->id = id;
426 
427   if (font->id & FONT_FULLWIDTH) {
428     cols = 2;
429   } else {
430     cols = 1;
431   }
432 
433   if (IS_ISCII(FONT_CS(font->id)) || FONT_CS(font->id) == ISO10646_UCS4_1_V) {
434     /*
435      * For exampe, 'W' width and 'l' width of OR-TTSarala font for ISCII_ORIYA
436      * are the same by chance, though it is actually a proportional font.
437      */
438     font->is_var_col_width = font->is_proportional = 1;
439   } else if (font_present & FONT_VAR_WIDTH) {
440     font->is_var_col_width = 1;
441   }
442 
443   if (font_present & FONT_VERTICAL) {
444     font->is_vertical = 1;
445   }
446 
447   if ((wincsinfo = get_wincs_info(FONT_CS(font->id))) == NULL) {
448 #ifdef DEBUG
449     bl_warn_printf(BL_DEBUG_TAG " charset(0x%.2x) is not supported.\n", FONT_CS(font->id));
450 #endif
451 
452     free(font);
453 
454     return NULL;
455   }
456 
457   if (font->id & FONT_BOLD) {
458 #if 0
459     weight = FW_BOLD;
460 #else
461     /*
462      * XXX
463      * Width of bold font is not necessarily the same as
464      * that of normal font in win32.
465      * So ignore weight and if font->id is bold use double-
466      * drawing method for now.
467      */
468     weight = FW_DONTCARE;
469 #endif
470   } else {
471     weight = FW_MEDIUM;
472   }
473 
474   if (font->id & FONT_ITALIC) {
475     is_italic = TRUE;
476   } else {
477     is_italic = FALSE;
478   }
479 
480   if (size_attr) {
481     if (size_attr >= DOUBLE_HEIGHT_TOP) {
482       fontsize *= 2;
483     }
484 
485     col_width *= 2;
486   }
487 
488   font_family = NULL;
489   percent = 0;
490   fontsize_d = (double)fontsize;
491 
492   if (FONT_CS(font->id) == DEC_SPECIAL) {
493     font_family = "Tera Special";
494   } else if (fontname) {
495     char *p;
496 
497     if ((p = alloca(strlen(fontname) + 1)) == NULL) {
498       free(font);
499 
500       return NULL;
501     }
502     strcpy(p, fontname);
503 
504     parse_font_name(&font_family, &weight, &is_italic, &fontsize_d, &percent, p);
505   } else {
506     /* Default font */
507     font_family = "Courier New";
508   }
509 
510   wlen = MultiByteToWideChar(CP_UTF8, 0, font_family, -1, NULL, 0);
511   if ((w_font_family = alloca(wlen)) == NULL) {
512     free(font);
513 
514     return NULL;
515   }
516   MultiByteToWideChar(CP_UTF8, 0, font_family, -1, w_font_family, wlen);
517 
518   if (!display_gc) {
519     display_gc = CreateIC("Display", NULL, NULL, NULL);
520   }
521 
522   height = fontsize_d;
523 #if 0
524   if (col_width > 0) {
525     if (size_attr == DOUBLE_WIDTH) {
526       width = fontsize_d;
527     } else {
528       width = (font->is_vertical ? col_width / 2 : col_width);
529     }
530   }
531 #else
532   if (size_attr == DOUBLE_WIDTH) {
533     width = fontsize_d;
534   }
535 #endif
536   else {
537     width = 0;
538   }
539   if (use_point_size) {
540     height = -MulDiv(height, GetDeviceCaps(display_gc, LOGPIXELSY), 72);
541     width = -MulDiv(width, GetDeviceCaps(display_gc, LOGPIXELSX), 72);
542   }
543 
544   font->xfont->fid =
545       CreateFontW(height, /* Height */
546                   width,  /* Width (0=auto) */
547                   0,      /* text angle */
548                   0,      /* char angle */
549                   weight, /* weight */
550                   is_italic, /* italic */
551                   FALSE,  /* underline */
552                   FALSE,  /* eraseline */
553                   DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
554                   (font_present & FONT_AA) ? ANTIALIASED_QUALITY :
555                                              DEFAULT_QUALITY /* PROOF_QUALITY */,
556                   FIXED_PITCH | FF_DONTCARE, w_font_family);
557 
558   if (!font->xfont->fid) {
559 #ifdef DEBUG
560     bl_warn_printf(BL_DEBUG_TAG " CreateFont failed.\n");
561     free(font);
562 #endif
563 
564     return NULL;
565   } else {
566     TEXTMETRIC tm;
567     SIZE w_sz;
568     SIZE l_sz;
569 
570     SelectObject(display_gc, font->xfont->fid);
571 
572     GetTextMetrics(display_gc, &tm);
573 
574     /*
575      * Note that fixed pitch font containing both Hankaku and Zenkaku characters
576      * like
577      * MS Gothic is regarded as VARIABLE_PITCH. (tm.tmPitchAndFamily)
578      * So "w" and "l" width is compared to check if font is proportional or not.
579      */
580     GetTextExtentPoint32A(display_gc, "w", 1, &w_sz);
581     GetTextExtentPoint32A(display_gc, "l", 1, &l_sz);
582 
583 #ifdef DEBUG
584     bl_debug_printf(
585         "Family %s Size %d CS %x => AveCharWidth %d MaxCharWidth %d 'w' width "
586         "%d 'l' width %d Height %d Ascent %d ExLeading %d InLeading %d "
587         "Pitch&Family %d Weight %d\n",
588         font_family, fontsize, wincsinfo->cs, tm.tmAveCharWidth, tm.tmMaxCharWidth, w_sz.cx,
589         l_sz.cx, tm.tmHeight, tm.tmAscent, tm.tmExternalLeading, tm.tmInternalLeading,
590         tm.tmPitchAndFamily, tm.tmWeight);
591 #endif
592 
593     if (w_sz.cx != l_sz.cx) {
594       font->is_proportional = 1;
595       font->width = tm.tmAveCharWidth * cols;
596     } else {
597       SIZE sp_sz;
598       WORD sp = 0x3000; /* wide space */
599 
600       if (cols == 2 && GetTextExtentPoint32W(display_gc, &sp, 1, &sp_sz) &&
601           sp_sz.cx != l_sz.cx * 2) {
602         font->is_proportional = 1;
603         font->width = sp_sz.cx;
604       } else {
605         font->width = w_sz.cx * cols;
606       }
607     }
608 
609     font->height = tm.tmHeight;
610     font->ascent = tm.tmAscent;
611 
612     if ((font->id & FONT_BOLD) && tm.tmWeight <= FW_MEDIUM) {
613       font->double_draw_gap = 1;
614     }
615   }
616 
617   font->xfont->size = fontsize;
618 
619   /*
620    * Following processing is same as ui_font.c:set_xfont()
621    */
622 
623   if (col_width == 0) {
624     /* standard(usascii) font */
625 
626     if (!font->is_var_col_width) {
627       u_int ch_width;
628 
629       if (percent > 0) {
630         if (font->is_vertical) {
631           /* The width of full and half character font is the same. */
632           letter_space *= 2;
633           ch_width = fontsize * percent / 100;
634         } else {
635           ch_width = fontsize * percent / 200;
636         }
637       } else {
638         ch_width = font->width;
639 
640         if (font->is_vertical) {
641           /* The width of full and half character font is the same. */
642           letter_space *= 2;
643           ch_width *= 2;
644         }
645       }
646 
647       if (use_point_size) {
648         ch_width = -MulDiv(ch_width, GetDeviceCaps(display_gc, LOGPIXELSX), 72);
649       }
650 
651       if (letter_space > 0 || ch_width > -letter_space) {
652         ch_width += letter_space;
653       }
654 
655       if (font->width != ch_width) {
656         font->is_proportional = 1;
657 
658         if (font->width < ch_width) {
659           font->x_off = (ch_width - font->width) / 2;
660         }
661 
662         font->width = ch_width;
663       }
664     }
665   } else {
666     /* not a standard(usascii) font */
667 
668     /*
669      * XXX hack
670      * forcibly conforming non standard font width to standard font width.
671      */
672 
673     if (font->is_vertical) {
674       /*
675        * The width of full and half character font is the same.
676        * is_var_col_width is always false if is_vertical is true.
677        */
678       if (font->width != col_width) {
679         bl_msg_printf("Font(id %x) width(%d) doesn't fit column width(%d).\n",
680                       font->id, font->width, col_width);
681 
682         font->is_proportional = 1;
683 
684         if (font->width < col_width) {
685           font->x_off = (col_width - font->width) / 2;
686         }
687 
688         font->width = col_width;
689       }
690     } else if (font->width != col_width * cols) {
691       bl_msg_printf("Font(id %x) width(%d) doesn't fit column width(%d).\n",
692                     font->id, font->width, col_width * cols);
693 
694       font->is_proportional = 1;
695 
696       if (!font->is_var_col_width) {
697         if (font->width < col_width * cols) {
698           font->x_off = (col_width * cols - font->width) / 2;
699         }
700 
701         font->width = col_width * cols;
702       }
703     }
704   }
705 
706   font->size_attr = size_attr;
707 
708   if (wincsinfo->cs != ANSI_CHARSET && wincsinfo->cs != SYMBOL_CHARSET &&
709       !IS_ISO10646_UCS4(FONT_CS(font->id))) {
710     if (!(font->xfont->conv = vt_char_encoding_conv_new(wincsinfo->encoding))) {
711 #ifdef DEBUG
712       bl_warn_printf(BL_DEBUG_TAG " vt_char_encoding_conv_new(font id %x) failed.\n", font->id);
713 #endif
714     }
715   }
716 
717   if (font->is_proportional && !font->is_var_col_width) {
718     bl_msg_printf("Font(id %x): Draw one char at a time to fit column width.\n", font->id);
719   }
720 
721   return font;
722 }
723 
ui_font_destroy(ui_font_t * font)724 void ui_font_destroy(ui_font_t *font) {
725 #ifdef USE_OT_LAYOUT
726   if (font->ot_font) {
727     otl_close(font->ot_font);
728   }
729 #endif
730 
731   if (font->xfont->fid) {
732     DeleteObject(font->xfont->fid);
733   }
734 
735   if (font->xfont->conv) {
736     (*font->xfont->conv->destroy)(font->xfont->conv);
737   }
738 
739   free(font);
740 
741   if (display_gc) {
742     DeleteDC(display_gc);
743     display_gc = None;
744   }
745 }
746 
747 #ifdef USE_OT_LAYOUT
ui_font_has_ot_layout_table(ui_font_t * font)748 int ui_font_has_ot_layout_table(ui_font_t *font) {
749   if (!font->ot_font) {
750     if (font->ot_font_not_found) {
751       return 0;
752     }
753 
754     if (!display_gc) {
755       /*
756        * Cached as far as ui_caculate_char_width is called.
757        * display_gc is destroyed in ui_font_new or ui_font_destroy.
758        */
759       display_gc = CreateIC("Display", NULL, NULL, NULL);
760     }
761 
762     SelectObject(display_gc, font->xfont->fid);
763 
764     font->ot_font = otl_open(display_gc);
765 
766     if (!font->ot_font) {
767       font->ot_font_not_found = 1;
768 
769       return 0;
770     }
771   }
772 
773   return 1;
774 }
775 
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)776 u_int ui_convert_text_to_glyphs(ui_font_t *font, u_int32_t *shape_glyphs, u_int num_shape_glyphs,
777                                 int8_t *xoffsets, int8_t *yoffsets, u_int8_t *advances,
778                                 u_int32_t *noshape_glyphs, u_int32_t *src, u_int src_len,
779                                 const char *script, const char *features) {
780   u_int size;
781 
782   if (use_point_size) {
783     if (!display_gc) {
784       display_gc = CreateIC("Display", NULL, NULL, NULL);
785     }
786 
787     size = MulDiv((int)font->xfont->size, GetDeviceCaps(display_gc, LOGPIXELSY), 72);
788   } else {
789     size = font->xfont->size;
790   }
791 
792   return otl_convert_text_to_glyphs(font->ot_font, shape_glyphs, num_shape_glyphs,
793                                     xoffsets, yoffsets, advances, noshape_glyphs,
794                                     src, src_len, script, features,
795                                     size * (font->size_attr >= DOUBLE_WIDTH ? 2 : 1));
796 }
797 #endif /* USE_OT_LAYOUT */
798 
ui_calculate_char_width(ui_font_t * font,u_int32_t ch,ef_charset_t cs,int is_awidth,int * draw_alone)799 u_int ui_calculate_char_width(ui_font_t *font, u_int32_t ch, ef_charset_t cs, int is_awidth,
800                               int *draw_alone) {
801   if (draw_alone) {
802     *draw_alone = 0;
803   }
804 
805   if (font->is_proportional) {
806     if (font->is_var_col_width) {
807       return calculate_char_width(font, ch, cs);
808     }
809 
810     if (draw_alone) {
811       *draw_alone = 1;
812     }
813   } else if (draw_alone &&
814              cs == ISO10646_UCS4_1 /* ISO10646_UCS4_1_V is always proportional */ &&
815              is_awidth) {
816     if (calculate_char_width(font, ch, cs) != font->width) {
817       *draw_alone = 1;
818     }
819   }
820 
821   return font->width;
822 }
823 
ui_font_use_point_size(int use)824 void ui_font_use_point_size(int use) { use_point_size = use; }
825 
826 /* Return written size */
ui_convert_ucs4_to_utf16(u_char * dst,u_int32_t src)827 size_t ui_convert_ucs4_to_utf16(u_char *dst, /* 4 bytes. Little endian. 16bit aligned. */
828                                 u_int32_t src) {
829   u_int16_t *dst16 = (u_int16_t*)dst;
830 
831   if (src < 0x10000) {
832     *dst16 = src;
833 
834     return 2;
835   } else if (src < 0x110000) {
836     /* surrogate pair */
837 
838     src -= 0x10000;
839 
840     dst16[0] = ((src & 0xfffc0000) >> 10) + 0xd800 + ((src & 0x3fc00) >> 10);
841     dst16[1] = (src & 0x300) + 0xdc00 + (src & 0xff);
842 
843     return 4;
844   }
845 
846   return 0;
847 }
848 
849 #ifdef DEBUG
850 
ui_font_dump(ui_font_t * font)851 void ui_font_dump(ui_font_t *font) {
852   bl_msg_printf("  id %x: Font %p", font->id, font->xfont->fid);
853 
854   if (font->is_proportional) {
855     bl_msg_printf(" (proportional)");
856   }
857   bl_msg_printf("\n");
858 }
859 
860 #endif
861