/* -*-c-*- */ /* Copyright (C) 2002 Olivier Chapuis */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* FlocaleRotateDrawString is strongly inspired by some part of xvertext * taken from wmx */ /* Here the copyright for this function: */ /* xvertext, Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma) * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both the * copyright notice and this permission notice appear in supporting * documentation. All work developed as a consequence of the use of * this program should duly acknowledge such use. No representations are * made about the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. * * Minor modifications by Chris Cannam for wm2/wmx * Major modifications by Kazushi (Jam) Marukawa for wm2/wmx i18n patch * Simplification and complications by olicha for use with fvwm */ /* ---------------------------- included header files ---------------------- */ #include "config.h" #include #include #include #include #include #include "defaults.h" #include "fvwmlib.h" #include "Graphics.h" #include "ColorUtils.h" #include "Strings.h" #include "Parse.h" #include "PictureBase.h" #include "Flocale.h" #include "FlocaleCharset.h" #include "FBidi.h" #include "FftInterface.h" #include "Colorset.h" #include "Ficonv.h" #include "CombineChars.h" /* ---------------------------- local definitions -------------------------- */ /* ---------------------------- local macros ------------------------------- */ #define FSwitchDrawString(use_16, dpy, d, gc, x, y, s8, s16, l) \ (use_16) ? \ XDrawString16(dpy, d, gc, x, y, s16, l) : \ XDrawString(dpy, d, gc, x, y, s8, l) #define FSwitchDrawImageString(use_16, dpy, d, gc, x, y, s8, s16, l) \ (use_16) ? \ XDrawImageString16(dpy, d, gc, x, y, s16, l) : \ XDrawImageString(dpy, d, gc, x, y, s8, l) /* ---------------------------- imports ------------------------------------ */ /* ---------------------------- included code files ------------------------ */ /* ---------------------------- local types -------------------------------- */ /* ---------------------------- forward declarations ----------------------- */ /* ---------------------------- local variables ---------------------------- */ static FlocaleFont *FlocaleFontList = NULL; static char *Flocale = NULL; static char *Fmodifiers = NULL; /* TODO: make these (static const char *) */ static char *fft_fallback_font = FLOCALE_FFT_FALLBACK_FONT; static char *mb_fallback_font = FLOCALE_MB_FALLBACK_FONT; static char *fallback_font = FLOCALE_FALLBACK_FONT; /* ---------------------------- exported variables (globals) --------------- */ /* ---------------------------- local functions ---------------------------- */ /* * shadow local functions */ static void FlocaleParseShadow(char *str, int *shadow_size, int *shadow_offset, int *direction, char * fontname, char *module) { char *dir_str; char *token; multi_direction_t dir; *direction = MULTI_DIR_NONE; token = PeekToken(str, &dir_str); if (token == NULL || *token == 0 || (GetIntegerArguments(token, NULL, shadow_size, 1) != 1) || *shadow_size < 0) { *shadow_size = 0; fprintf(stderr,"[%s][FlocaleParseShadow]: WARNING -- bad " "shadow size in font name:\n\t'%s'\n", (module)? module: "fvwm", fontname); return; } if (*shadow_size == 0) { return; } /* some offset ? */ if (dir_str && *dir_str && (GetIntegerArguments(dir_str, NULL, shadow_offset, 1) == 1)) { if (*shadow_offset < 0) { *shadow_offset = 0; fprintf(stderr,"[%s][FlocaleParseShadow]: WARNING -- " "bad shadow offset in font name:\n\t'%s'\n", (module)? module: "fvwmlibs", fontname); } PeekToken(dir_str, &dir_str); } while (dir_str && *dir_str && *dir_str != '\n') { dir = gravity_parse_multi_dir_argument(dir_str, &dir_str); if (dir == MULTI_DIR_NONE) { fprintf(stderr,"[%s][FlocaleParseShadow]: WARNING -- " "bad shadow direction in font description:\n" "\t%s\n", (module)? module: "fvwmlibs", fontname); PeekToken(dir_str, &dir_str); /* skip it */ } else { *direction |= dir; } } if (*direction == MULTI_DIR_NONE) *direction = MULTI_DIR_SE; } /* * some simple converters */ static int FlocaleChar2bOneCharToUtf8(XChar2b c, char *buf) { int len; char byte1 = c.byte1; char byte2 = c.byte2; unsigned short ucs2 = ((unsigned short)byte1 << 8) + byte2; if(ucs2 <= 0x7f) { len = 1; buf[0] = (char)ucs2; buf[1] = 0; } else if(ucs2 <= 0x7ff) { len = 2; buf[0] = (ucs2 >> 6) | 0xc0; buf[1] = (ucs2 & 0x3f) | 0x80; buf[2] = 0; } else { len = 3; buf[0] = (ucs2 >> 12) | 0xe0; buf[1] = ((ucs2 & 0xfff) >> 6) | 0x80; buf[2] = (ucs2 & 0x3f) | 0x80; buf[3] = 0; } return len; } /* return number of bytes of character at current position (pointed to by str) */ int FlocaleStringNumberOfBytes(FlocaleFont *flf, const char *str) { int bytes = 0; if(FLC_ENCODING_TYPE_IS_UTF_8(flf->fc)) { /* handle UTF-8 */ if ((str[0] & 0x80) == 0) { bytes = 1; } else if((str[0] & ~0xdf) == 0) { bytes = 2; } else { /* this handles only 16-bit Unicode */ bytes = 3; } } else if(flf->flags.is_mb) { /* non-UTF-8 multibyte encoding */ if ((str[0] & 0x80) == 0) { bytes = 1; } else { bytes = 2; } } else { /* we must be using an "ordinary" 8-bit encoding */ bytes = 1; } return bytes; } /* given a string, font specifying its locale and a byte offset gives character offset */ int FlocaleStringByteToCharOffset(FlocaleFont *flf, const char *str, int offset) { const char *curr_ptr = str; int i = 0; int len = strlen(str); int coffset = 0; int curr_len; for(i = 0 ; i < offset && i < len ; i += curr_len, curr_ptr += curr_len, coffset++) { curr_len = FlocaleStringNumberOfBytes(flf, curr_ptr); } return coffset; } /* like above but reversed, ie. return byte offset corresponding to given charater offset */ int FlocaleStringCharToByteOffset(FlocaleFont *flf, const char *str, int coffset) { const char *curr_ptr = str; int i; int len = strlen(str); int offset = 0; int curr_len; for(i = 0 ; i < coffset && i < len ; offset += curr_len, curr_ptr += curr_len, i++) { curr_len = FlocaleStringNumberOfBytes(flf, curr_ptr); } return offset; } /* return length of string in characters */ int FlocaleStringCharLength(FlocaleFont *flf, const char *str) { int i, len; int str_len = strlen(str); for(i = 0, len = 0 ; i < str_len ; i += FlocaleStringNumberOfBytes(flf, str+i), len++); return len; } static XChar2b *FlocaleUtf8ToUnicodeStr2b(char *str, int len, int *nl) { XChar2b *str2b = NULL; int i = 0, j = 0, t; str2b = (XChar2b *)safemalloc((len+1)*sizeof(XChar2b)); while (i < len && str[i] != 0) { if ((str[i] & 0x80) == 0) { str2b[j].byte2 = str[i]; str2b[j].byte1 = 0; } else if ((str[i] & ~0xdf) == 0 && i+1 < len) { t = ((str[i] & 0x1f) << 6) + (str[i+1] & 0x3f); str2b[j].byte2 = (unsigned char)(t & 0xff); str2b[j].byte1 = (unsigned char)(t >> 8); i++; } else if (i+2 > 8); i += 2; } i++; j++; } *nl = j; return str2b; } /* Note: this function is not expected to work; good mb rendering * should be (and is) done using Xmb functions and not XDrawString16 * (or with iso10646-1 fonts and setting the encoding). * This function is used when the locale does not correspond to the font. * It works with "EUC fonts": ksc5601.1987-0, gb2312 and maybe also * cns11643-*. It works patially with jisx* and big5-0. Should try gbk-0, * big5hkscs-0, and cns-11643- */ static XChar2b *FlocaleStringToString2b( Display *dpy, FlocaleFont *flf, char *str, int len, int *nl) { XChar2b *str2b = NULL; char *tmp = NULL; Bool free_str = False; int i = 0, j = 0; Bool euc = True; /* KSC5601 (EUC-KR), GB2312 (EUC-CN), CNS11643-1986-1 * (EUC-TW) and converted jisx (EUC-JP) */ if (flf->fc && StrEquals(flf->fc->x,"jisx0208.1983-0")) { tmp = FiconvCharsetToCharset( dpy, flf->fc, FlocaleCharsetGetEUCJPCharset(), str, len); if (tmp != NULL) { free_str = True; str = tmp; len = strlen(tmp); } } else if (flf->fc && StrEquals(flf->fc->x,"big5-0")) { euc = False; } str2b = (XChar2b *)safemalloc((len+1)*sizeof(XChar2b)); if (euc) { while (i < len && str[i] != 0) { if ((str[i] & 0x80) == 0) { /* seems ok with KSC5601 and GB2312 as we get * almost the ascii. I do no try * CNS11643-1986-1. Should convert to ascii * with jisx */ str2b[j].byte1 = 0x23; /* magic number! */ str2b[j].byte2 = str[i++]; } else if (i+1 < len) { /* mb gl (for gr replace & 0x7f by | 0x80 ...) */ str2b[j].byte1 = str[i++] & 0x7f; str2b[j].byte2 = str[i++] & 0x7f; } else { str2b[j].byte1 = 0; str2b[j].byte2 = 0; i++; } j++; } } else /* big5 and others not yet tested */ { while (i < len && str[i] != 0) { if ((str[i] & 0x80) == 0) { /* we should convert to ascii */ #if 0 str2b[j].byte1 = 0xa2; /* magic number! */ str2b[j].byte2 = str[i++]; #endif /* a blanck char ... */ str2b[j].byte1 = 0x21; str2b[j].byte2 = 0x21; } else if (i+1 < len) { str2b[j].byte1 = str[i++]; str2b[j].byte2 = str[i++]; } else { str2b[j].byte1 = 0; str2b[j].byte2 = 0; i++; } j++; } } *nl = j; if (free_str) free(str); return str2b; } static char *FlocaleEncodeString( Display *dpy, FlocaleFont *flf, char *str, int *do_free, int len, int *nl, int *is_rtl, superimpose_char_t **comb_chars, int **l_to_v) { char *str1, *str2, *str3; int len1; int len2; int i; Bool do_iconv = True; const char *bidi_charset; len1 = len; len2 = 0; if (is_rtl != NULL) *is_rtl = False; *do_free = False; *nl = len; if (flf->str_fc == NULL || flf->fc == NULL || flf->fc == flf->str_fc) { do_iconv = False; } str1 = str; if (FiconvSupport) { char *tmp_str; /* first process combining characters */ tmp_str = FiconvCharsetToUtf8( dpy, flf->str_fc, (const char *)str,len); /* if conversion to UTF-8 failed str1 will be NULL */ if(tmp_str != NULL) { /* do combining */ len = CombineChars((unsigned char *)tmp_str, strlen(tmp_str), comb_chars, l_to_v); /* returns the length of the resulting UTF-8 string */ /* convert back to current charset */ str1 = FiconvUtf8ToCharset( dpy, flf->str_fc, (const char *)tmp_str,len); if (tmp_str != str1) { free(tmp_str); } if (str1) { *nl = len = strlen(str1); *do_free = True; } else { /* convert back to current charset fail */ len = strlen(str); str1 = str; } } } if (FiconvSupport && do_iconv) { str2 = FiconvCharsetToCharset( dpy, flf->str_fc, flf->fc, (const char *)str1, len); if (str2 == NULL) { /* fail to convert */ return str1; } if (str2 != str1) { if (*do_free && str1) { free(str1); str1 = str2; } *do_free = True; len1 = strlen(str2); } } else { str2 = str1; len1 = len; /* initialise array with composing characters (empty) */ if(comb_chars != NULL && *comb_chars == NULL) { *comb_chars = (superimpose_char_t *) safemalloc(sizeof(superimpose_char_t)); (*comb_chars)[0].position = -1; (*comb_chars)[0].c.byte1 = 0; (*comb_chars)[0].c.byte2 = 0; } /* initialise logic to visual mapping here if that is demanded (this is default when no combining has been done (1-to-1)) */ if(l_to_v != NULL && *l_to_v == NULL) { *l_to_v = (int*)safemalloc((len + 1) * sizeof(int)); for(i = 0 ; i < len ; i++) (*l_to_v)[i] = i; (*l_to_v)[len] = -1; } } if (FlocaleGetBidiCharset(dpy, flf->str_fc) != NULL && (bidi_charset = FlocaleGetBidiCharset(dpy, flf->fc)) != NULL) { str3 = FBidiConvert(str2, bidi_charset, len1, is_rtl, &len2, comb_chars != NULL ? *comb_chars : NULL, l_to_v != NULL ? *l_to_v : NULL); if (str3 != NULL && str3 != str2) { if (*do_free) { free(str2); } *do_free = True; len1 = len2; str1 = str3; } /* if we failed to do BIDI convert, return string string from combining phase */ else { str1 = str2; /* we already have the logical to visual mapping from combining phase */ } } *nl = len1; return str1; } static void FlocaleEncodeWinString( Display *dpy, FlocaleFont *flf, FlocaleWinString *fws, int *do_free, int *len, superimpose_char_t **comb_chars, int **l_to_v) { int len2b; fws->e_str = FlocaleEncodeString( dpy, flf, fws->str, do_free, *len, len, NULL, comb_chars, l_to_v); fws->str2b = NULL; if (flf->font != None) { if (FLC_ENCODING_TYPE_IS_UTF_8(flf->fc)) { fws->str2b = FlocaleUtf8ToUnicodeStr2b( fws->e_str, *len, &len2b); } else if (flf->flags.is_mb) { fws->str2b = FlocaleStringToString2b( dpy, flf, fws->e_str, *len, &len2b); } } } /* * Text Drawing with a FontStruct */ static void FlocaleFontStructDrawString( Display *dpy, FlocaleFont *flf, Drawable d, GC gc, int x, int y, Pixel fg, Pixel fgsh, Bool has_fg_pixels, FlocaleWinString *fws, int len, Bool image) { int xt = x; int yt = y; int is_string16; flocale_gstp_args gstp_args; is_string16 = (FLC_ENCODING_TYPE_IS_UTF_8(flf->fc) || flf->flags.is_mb); if (is_string16 && fws->str2b == NULL) { return; } if (image) { /* for rotated drawing */ FSwitchDrawImageString( is_string16, dpy, d, gc, x, y, fws->e_str, fws->str2b, len); } else { FlocaleInitGstpArgs(&gstp_args, flf, fws, x, y); /* normal drawing */ if (flf->shadow_size != 0 && has_fg_pixels == True) { XSetForeground(dpy, fws->gc, fgsh); while (FlocaleGetShadowTextPosition( &xt, &yt, &gstp_args)) { FSwitchDrawString( is_string16, dpy, d, gc, xt, yt, fws->e_str, fws->str2b, len); } } if (has_fg_pixels == True) { XSetForeground(dpy, gc, fg); } xt = gstp_args.orig_x; yt = gstp_args.orig_y; FSwitchDrawString( is_string16, dpy, d, gc, xt,yt, fws->e_str, fws->str2b, len); } return; } /* * Rotated Text Drawing with a FontStruct or a FontSet */ static void FlocaleRotateDrawString( Display *dpy, FlocaleFont *flf, FlocaleWinString *fws, Pixel fg, Pixel fgsh, Bool has_fg_pixels, int len, superimpose_char_t *comb_chars, int *pixel_pos) { static GC my_gc = None; static GC font_gc = None; int j, i, xpfg, ypfg, xpsh, ypsh; unsigned char *normal_data, *rotated_data; unsigned int normal_w, normal_h, normal_len; unsigned int rotated_w, rotated_h, rotated_len; char val; int width, height, descent, min_offset; XImage *image, *rotated_image; Pixmap canvas_pix, rotated_pix; flocale_gstp_args gstp_args; char buf[4]; if (fws->str == NULL || len < 1) { return; } if (fws->flags.text_rotation == ROTATION_0) { return; /* should not happen */ } if (my_gc == None) { my_gc = fvwmlib_XCreateGC(dpy, fws->win, 0, NULL); } XCopyGC(dpy, fws->gc, GCForeground|GCBackground, my_gc); /* width and height (no shadow!) */ width = FlocaleTextWidth(flf, fws->str, len) - FLF_SHADOW_WIDTH(flf); height = flf->height - FLF_SHADOW_HEIGHT(flf); descent = flf->descent - FLF_SHADOW_DESCENT(flf);; if (width < 1) width = 1; if (height < 1) height = 1; /* glyph width and height of the normal text */ normal_w = width; normal_h = height; /* width in bytes */ normal_len = (normal_w - 1) / 8 + 1; /* create and clear the canvas */ canvas_pix = XCreatePixmap(dpy, fws->win, width, height, 1); if (font_gc == None) { font_gc = fvwmlib_XCreateGC(dpy, canvas_pix, 0, NULL); } XSetBackground(dpy, font_gc, 0); XSetForeground(dpy, font_gc, 0); XFillRectangle(dpy, canvas_pix, font_gc, 0, 0, width, height); /* draw the character center top right on canvas */ XSetForeground(dpy, font_gc, 1); if (flf->font != NULL) { XSetFont(dpy, font_gc, flf->font->fid); FlocaleFontStructDrawString(dpy, flf, canvas_pix, font_gc, 0, height - descent, fg, fgsh, has_fg_pixels, fws, len, True); } else if (flf->fontset != None) { XmbDrawString( dpy, canvas_pix, flf->fontset, font_gc, 0, height - descent, fws->e_str, len); } /* here take care of superimposing chars */ i = 0; if(comb_chars != NULL) { while(comb_chars[i].c.byte1 != 0 && comb_chars[i].c.byte2 != 0) { /* draw composing character on top of corresponding "real" character */ FlocaleWinString tmp_fws = *fws; int offset = pixel_pos[comb_chars[i].position]; int curr_len = FlocaleChar2bOneCharToUtf8( comb_chars[i].c, buf); int out_len; char *buf2 = FiconvUtf8ToCharset( dpy, flf->str_fc, (const char *)buf,curr_len); if(buf2 == NULL) { /* if conversion failed, combinational char is not representable in current charset */ /* just replace with empty string */ buf2 = (char *)safemalloc(sizeof(char)); buf2[0] = 0; } tmp_fws.e_str = buf2; tmp_fws.str2b = NULL; if(flf->fontset != None) { XmbDrawString(dpy, canvas_pix, flf->fontset, fws->gc, offset, height - descent, buf2, strlen(buf2)); } else if(flf->font != None) { if (FLC_ENCODING_TYPE_IS_UTF_8(flf->fc)) { tmp_fws.str2b = (XChar2b *) safemalloc(2 * sizeof(XChar2b)); tmp_fws.str2b[0] = comb_chars[i].c; tmp_fws.str2b[1].byte1 = 0; tmp_fws.str2b[1].byte2 = 0; out_len = 1; } else if (flf->flags.is_mb) { tmp_fws.str2b = FlocaleStringToString2b( dpy, flf, tmp_fws.e_str, curr_len, &out_len); } else { out_len = strlen(buf2); } XSetFont(dpy, font_gc, flf->font->fid); FlocaleFontStructDrawString( dpy, flf, canvas_pix, font_gc, offset, height - descent, fg, fgsh, has_fg_pixels, &tmp_fws, out_len, True); } free(buf2); if(tmp_fws.str2b != NULL) { free(tmp_fws.str2b); } i++; } } /* reserve memory for the first XImage */ normal_data = (unsigned char *)safemalloc(normal_len * normal_h); /* create depth 1 XImage */ if ((image = XCreateImage( dpy, Pvisual, 1, XYBitmap, 0, (char *)normal_data, normal_w, normal_h, 8, 0)) == NULL) { return; } image->byte_order = image->bitmap_bit_order = MSBFirst; /* extract character from canvas */ XGetSubImage( dpy, canvas_pix, 0, 0, normal_w, normal_h, 1, XYPixmap, image, 0, 0); image->format = XYBitmap; /* width, height of the rotated text */ if (fws->flags.text_rotation == ROTATION_180) { rotated_w = normal_w; rotated_h = normal_h; } else /* vertical text */ { rotated_w = normal_h; rotated_h = normal_w; } /* width in bytes */ rotated_len = (rotated_w - 1) / 8 + 1; /* reserve memory for the rotated image */ rotated_data = (unsigned char *)safecalloc(rotated_h * rotated_len, 1); /* create the rotated X image */ if ((rotated_image = XCreateImage( dpy, Pvisual, 1, XYBitmap, 0, (char *)rotated_data, rotated_w, rotated_h, 8, 0)) == NULL) { return; } rotated_image->byte_order = rotated_image->bitmap_bit_order = MSBFirst; /* map normal text data to rotated text data */ for (j = 0; j < rotated_h; j++) { for (i = 0; i < rotated_w; i++) { /* map bits ... */ if (fws->flags.text_rotation == ROTATION_270) val = normal_data[ i * normal_len + (normal_w - j - 1) / 8 ] & (128 >> ((normal_w - j - 1) % 8)); else if (fws->flags.text_rotation == ROTATION_180) val = normal_data[ (normal_h - j - 1) * normal_len + (normal_w - i - 1) / 8 ] & (128 >> ((normal_w - i - 1) % 8)); else /* ROTATION_90 */ val = normal_data[ (normal_h - i - 1) * normal_len + j / 8] & (128 >> (j % 8)); if (val) rotated_data[j * rotated_len + i / 8] |= (128 >> (i % 8)); } } /* create the character's bitmap and put the image on it */ rotated_pix = XCreatePixmap(dpy, fws->win, rotated_w, rotated_h, 1); XPutImage( dpy, rotated_pix, font_gc, rotated_image, 0, 0, 0, 0, rotated_w, rotated_h); /* free the image and data */ XDestroyImage(image); XDestroyImage(rotated_image); /* free pixmap and GC */ XFreePixmap(dpy, canvas_pix); /* x and y corrections: we fill a rectangle! */ min_offset = FlocaleGetMinOffset(flf, fws->flags.text_rotation); switch (fws->flags.text_rotation) { case ROTATION_90: /* CW */ xpfg = fws->x - min_offset; ypfg = fws->y; break; case ROTATION_180: xpfg = fws->x; ypfg = fws->y - min_offset + FLF_SHADOW_BOTTOM_SIZE(flf); break; case ROTATION_270: /* CCW */ xpfg = fws->x - min_offset; ypfg = fws->y; break; case ROTATION_0: default: xpfg = fws->x; ypfg = fws->y - min_offset; break; } xpsh = xpfg; ypsh = ypfg; /* write the image on the window */ XSetFillStyle(dpy, my_gc, FillStippled); XSetStipple(dpy, my_gc, rotated_pix); FlocaleInitGstpArgs(&gstp_args, flf, fws, xpfg, ypfg); if (flf->shadow_size != 0 && has_fg_pixels == True) { XSetForeground(dpy, my_gc, fgsh); while (FlocaleGetShadowTextPosition(&xpsh, &ypsh, &gstp_args)) { XSetTSOrigin(dpy, my_gc, xpsh, ypsh); XFillRectangle( dpy, fws->win, my_gc, xpsh, ypsh, rotated_w, rotated_h); } } xpsh = gstp_args.orig_x; ypsh = gstp_args.orig_y; XSetTSOrigin(dpy, my_gc, xpsh, ypsh); XFillRectangle(dpy, fws->win, my_gc, xpsh, ypsh, rotated_w, rotated_h); XFreePixmap(dpy, rotated_pix); return; } /* * Fonts info and checking */ static char *FlocaleGetFullNameOfFontStruct(Display *dpy, XFontStruct *font) { char *full_name = NULL; unsigned long value; if (XGetFontProperty(font, XA_FONT, &value)) { full_name = XGetAtomName(dpy, value); } return full_name; } static char *FlocaleGetCharsetOfFontStruct(Display *dpy, XFontStruct *font) { int i = 0; int count = 0; char *charset = NULL; char *full_name; full_name = FlocaleGetFullNameOfFontStruct(dpy, font); if (full_name == NULL) { return NULL; } while(full_name[i] != '\0' && count < 13) { if (full_name[i] == '-') { count++; } i++; } if (count != 13) { return NULL; } CopyString(&charset, full_name+i); XFree(full_name); return charset; } static char *FlocaleGetCharsetFromName(char *name) { int l,i,e; char *charset; l = strlen(name); i = l-1; while(i >= 0 && name[i] != '-') { i--; } if (i == 0 || i == l-1) { return NULL; } i--; e = i; while(i >= 0 && name[i] != '-') { i--; } if (i <= 0 || e == i) { return NULL; } CopyString(&charset, name + i + 1); return charset; } /* return NULL if it is not reasonable to load a FontSet. * Currently return name if it is reasonable to load a FontSet, but in the * future we may want to transform name for faster FontSet loading */ static char *FlocaleFixNameForFontSet(Display *dpy, char *name, char *module) { char *new_name; char *charset; XFontStruct *test_font = NULL; if (!name) { return NULL; } new_name = name; if (strchr(name, ',')) { /* tmp, do not handle "," separated list */ return name; } charset = FlocaleGetCharsetFromName(name); if (charset == NULL && !strchr(name, '*') && !strchr(name, '?')) { /* probably a font alias! */ if ((test_font = XLoadQueryFont(dpy, name))) { charset = FlocaleGetCharsetOfFontStruct(dpy, test_font); XFreeFont(dpy, test_font); } } if (charset != NULL) { if (!strchr(charset, '*') && !strchr(charset, '?') && !FlocaleCharsetIsCharsetXLocale(dpy, charset, module)) { /* if the charset is fully specified and do not match * one of the X locale charset */ new_name = NULL; #if 0 fprintf(stderr,"[%s][FlocaleGetFontSet]: WARNING -- " "Use of a non X locale charset '%s' when " "loading font: %s\n", (module)? module:"fvwmlibs", charset, name); #endif } free(charset); } return new_name; } /* * Fonts loading */ static FlocaleFont *FlocaleGetFftFont( Display *dpy, char *fontname, char *encoding, char *module) { FftFontType *fftf = NULL; FlocaleFont *flf = NULL; char *fn, *hints = NULL; hints = GetQuotedString(fontname, &fn, "/", NULL, NULL, NULL); if (fn == NULL) { fn = fft_fallback_font; } else if (*fn == '\0') { free(fn); fn = fft_fallback_font; } fftf = FftGetFont(dpy, fn, module); if (fftf == NULL) { if (fn != NULL && fn != fft_fallback_font) { free(fn); } return NULL; } flf = (FlocaleFont *)safemalloc(sizeof(FlocaleFont)); memset(flf, '\0', sizeof(FlocaleFont)); flf->count = 1; flf->fftf = *fftf; FlocaleCharsetSetFlocaleCharset(dpy, flf, hints, encoding, module); FftGetFontHeights( &flf->fftf, &flf->height, &flf->ascent, &flf->descent); FftGetFontWidths(flf, &flf->max_char_width); free(fftf); if (fn != NULL && fn != fft_fallback_font) { free(fn); } return flf; } static FlocaleFont *FlocaleGetFontSet( Display *dpy, char *fontname, char *encoding, char *module) { static int mc_errors = 0; FlocaleFont *flf = NULL; XFontSet fontset = NULL; char **ml; int mc,i; char *ds; XFontSetExtents *fset_extents; char *fn, *hints = NULL, *fn_fixed = NULL; hints = GetQuotedString(fontname, &fn, "/", NULL, NULL, NULL); if (*fn == '\0') { free(fn); fn = fn_fixed = mb_fallback_font; } else if (!(fn_fixed = FlocaleFixNameForFontSet(dpy, fn, module))) { if (fn != NULL && fn != mb_fallback_font) { free(fn); } return NULL; } if (!(fontset = XCreateFontSet(dpy, fn_fixed, &ml, &mc, &ds))) { if (fn_fixed && fn_fixed != fn) { free(fn_fixed); } if (fn != NULL && fn != mb_fallback_font) { free(fn); } return NULL; } if (mc > 0) { if (mc_errors <= FLOCALE_NUMBER_MISS_CSET_ERR_MSG) { mc_errors++; fprintf(stderr, "[%s][FlocaleGetFontSet]: (%s)" " Missing font charsets:\n", (module)? module: "fvwmlibs", fontname); for (i = 0; i < mc; i++) { fprintf(stderr, "%s", ml[i]); if (i < mc - 1) fprintf(stderr, ", "); } fprintf(stderr, "\n"); if (mc_errors == FLOCALE_NUMBER_MISS_CSET_ERR_MSG) { fprintf(stderr, "[%s][FlocaleGetFontSet]: No more" " missing charset reportings\n", (module)? module: "fvwmlibs"); } } XFreeStringList(ml); } flf = (FlocaleFont *)safemalloc(sizeof(FlocaleFont)); memset(flf, '\0', sizeof(FlocaleFont)); flf->count = 1; flf->fontset = fontset; FlocaleCharsetSetFlocaleCharset(dpy, flf, hints, encoding, module); fset_extents = XExtentsOfFontSet(fontset); flf->height = fset_extents->max_ink_extent.height; flf->ascent = - fset_extents->max_ink_extent.y; flf->descent = fset_extents->max_ink_extent.height + fset_extents->max_ink_extent.y; flf->max_char_width = fset_extents->max_ink_extent.width; if (fn_fixed && fn_fixed != fn) { free(fn_fixed); } if (fn != NULL && fn != mb_fallback_font) { free(fn); } return flf; } static FlocaleFont *FlocaleGetFont( Display *dpy, char *fontname, char *encoding, char *module) { XFontStruct *font = NULL; FlocaleFont *flf; char *str,*fn,*tmp; char *hints = NULL; hints = GetQuotedString(fontname, &tmp, "/", NULL, NULL, NULL); str = GetQuotedString(tmp, &fn, ",", NULL, NULL, NULL); while (!font && fn) { if (*fn == '\0') { free(fn); fn = fallback_font; } font = XLoadQueryFont(dpy, fn); if (fn != NULL && fn != fallback_font) { free(fn); fn = NULL; } if (!font && str && *str) { str = GetQuotedString(str, &fn, ",", NULL, NULL, NULL); } } if (font == NULL) { if (fn != NULL && fn != fallback_font) { free(fn); } if (tmp != NULL) { free(tmp); } return NULL; } flf = (FlocaleFont *)safemalloc(sizeof(FlocaleFont)); memset(flf, '\0', sizeof(FlocaleFont)); flf->count = 1; flf->fontset = None; flf->fftf.fftfont = NULL; flf->font = font; FlocaleCharsetSetFlocaleCharset(dpy, flf, hints, encoding, module); flf->height = font->max_bounds.ascent + font->max_bounds.descent; flf->ascent = font->max_bounds.ascent; flf->descent = font->max_bounds.descent; flf->max_char_width = font->max_bounds.width; if (flf->font->max_byte1 > 0) flf->flags.is_mb = True; if (fn != NULL && fn != fallback_font) { free(fn); } if (tmp != NULL) { free(tmp); } return flf; } static FlocaleFont *FlocaleGetFontOrFontSet( Display *dpy, char *fontname, char *encoding, char *fullname, char *module) { FlocaleFont *flf = NULL; if (fontname && strlen(fontname) > 3 && strncasecmp("xft:", fontname, 4) == 0) { if (FftSupport) { flf = FlocaleGetFftFont( dpy, fontname+4, encoding, module); } if (flf) { CopyString(&flf->name, fullname); } return flf; } if (flf == NULL && Flocale != NULL && fontname) { flf = FlocaleGetFontSet(dpy, fontname, encoding, module); } if (flf == NULL && fontname) { flf = FlocaleGetFont(dpy, fontname, encoding, module); } if (flf && fontname) { if (StrEquals(fullname, mb_fallback_font)) { flf->name = mb_fallback_font; } else if (StrEquals(fullname, fallback_font)) { flf->name = fallback_font; } else { CopyString(&flf->name, fullname); } return flf; } return NULL; } /* * locale local functions */ static void FlocaleSetlocaleForX( int category, const char *locale, const char *module) { if ((Flocale = setlocale(category, locale)) == NULL) { fprintf(stderr, "[%s][%s]: ERROR -- Cannot set locale. Please check" " your $LC_CTYPE or $LANG.\n", (module == NULL)? "" : module, "FlocaleSetlocaleForX"); return; } if (!XSupportsLocale()) { fprintf(stderr, "[%s][%s]: WARNING -- X does not support locale %s\n", (module == NULL)? "": module, "FlocaleSetlocaleForX", Flocale); Flocale = NULL; } } /* ---------------------------- interface functions ------------------------ */ /* * locale initialisation */ void FlocaleInit( int category, const char *locale, const char *modifiers, const char *module) { FlocaleSetlocaleForX(category, locale, module); if (Flocale == NULL) return; if (modifiers != NULL && (Fmodifiers = XSetLocaleModifiers(modifiers)) == NULL) { fprintf(stderr, "[%s][%s]: WARNING -- Cannot set locale modifiers\n", (module == NULL)? "": module, "FlocaleInit"); } #if FLOCALE_DEBUG_SETLOCALE fprintf(stderr,"[%s][FlocaleInit] locale: %s, modifier: %s\n", module, Flocale, Fmodifiers); #endif } /* * fonts loading */ char *prefix_list[] = { "Shadow=", "StringEncoding=", NULL }; FlocaleFont *FlocaleLoadFont(Display *dpy, char *fontname, char *module) { FlocaleFont *flf = FlocaleFontList; Bool ask_default = False; char *t; char *str, *opt_str, *encoding= NULL, *fn = NULL; int shadow_size = 0; int shadow_offset = 0; int shadow_dir = MULTI_DIR_SE; int i; /* removing quoting for modules */ if (fontname && (t = strchr("\"'`", *fontname))) { char c = *t; fontname++; if (fontname[strlen(fontname)-1] == c) fontname[strlen(fontname)-1] = 0; } if (fontname == NULL || *fontname == 0) { ask_default = True; fontname = mb_fallback_font; } while (flf) { char *c1, *c2; for (c1 = fontname, c2 = flf->name; *c1 && *c2; ++c1, ++c2) { if (*c1 != *c2) { break; } } if (!*c1 && !*c2) { flf->count++; return flf; } flf = flf->next; } /* not cached load the font as a ";" separated list */ /* But first see if we have a shadow relief and/or an encoding */ str = fontname; while ((i = GetTokenIndex(str, prefix_list, -1, &str)) > -1) { str = GetQuotedString(str, &opt_str, ":", NULL, NULL, NULL); switch(i) { case 0: /* shadow= */ FlocaleParseShadow( opt_str, &shadow_size, &shadow_offset, &shadow_dir, fontname, module); break; case 1: /* encoding= */ if (encoding != NULL) { free(encoding); encoding = NULL; } if (opt_str && *opt_str) { CopyString(&encoding, opt_str); } break; default: break; } if (opt_str != NULL) free(opt_str); } if (str && *str) { str = GetQuotedString(str, &fn, ";", NULL, NULL, NULL); } else { fn = mb_fallback_font; } while (!flf && (fn && *fn)) { flf = FlocaleGetFontOrFontSet( dpy, fn, encoding, fontname, module); if (fn != NULL && fn != mb_fallback_font && fn != fallback_font) { free(fn); fn = NULL; } if (!flf && str && *str) { str = GetQuotedString(str, &fn, ";", NULL, NULL, NULL); } } if (fn != NULL && fn != mb_fallback_font && fn != fallback_font) { free(fn); } if (flf == NULL) { /* loading failed, try default font */ if (!ask_default) { fprintf(stderr,"[%s][FlocaleLoadFont]: " "WARNING -- can't load font '%s'," " trying default:\n", (module)? module: "fvwmlibs", fontname); } else { /* we already tried default fonts: try again? yes */ } if (Flocale != NULL) { if (!ask_default) { fprintf(stderr, "\t%s\n", mb_fallback_font); } if ((flf = FlocaleGetFontSet( dpy, mb_fallback_font, NULL, module)) != NULL) { flf->name = mb_fallback_font; } } if (flf == NULL) { if (!ask_default) { fprintf(stderr,"\t%s\n", fallback_font); } if ((flf = FlocaleGetFont( dpy, fallback_font, NULL, module)) != NULL) { flf->name = fallback_font; } else if (!ask_default) { fprintf(stderr, "[%s][FlocaleLoadFont]:" " ERROR -- can't load font.\n", (module)? module: "fvwmlibs"); } else { fprintf(stderr, "[%s][FlocaleLoadFont]: ERROR" " -- can't load default font:\n", (module)? module: "fvwmlibs"); fprintf(stderr, "\t%s\n", mb_fallback_font); fprintf(stderr, "\t%s\n", fallback_font); } } } if (flf != NULL) { if (shadow_size > 0) { flf->shadow_size = shadow_size; flf->flags.shadow_dir = shadow_dir; flf->shadow_offset = shadow_offset; flf->descent += FLF_SHADOW_DESCENT(flf); flf->ascent += FLF_SHADOW_ASCENT(flf); flf->height += FLF_SHADOW_HEIGHT(flf); flf->max_char_width += FLF_SHADOW_WIDTH(flf); } if (flf->fc == FlocaleCharsetGetUnknownCharset()) { fprintf(stderr,"[%s][FlocaleLoadFont]: " "WARNING -- Unknown charset for font\n\t'%s'\n", (module)? module: "fvwmlibs", flf->name); flf->fc = FlocaleCharsetGetDefaultCharset(dpy, module); } else if (flf->str_fc == FlocaleCharsetGetUnknownCharset() && (encoding != NULL || (FftSupport && flf->fftf.fftfont != NULL && flf->fftf.str_encoding != NULL))) { fprintf(stderr,"[%s][FlocaleLoadFont]: " "WARNING -- Unknown string encoding for font\n" "\t'%s'\n", (module)? module: "fvwmlibs", flf->name); } if (flf->str_fc == FlocaleCharsetGetUnknownCharset()) { flf->str_fc = FlocaleCharsetGetDefaultCharset(dpy, module); } flf->next = FlocaleFontList; FlocaleFontList = flf; } if (encoding != NULL) { free(encoding); } return flf; } void FlocaleUnloadFont(Display *dpy, FlocaleFont *flf) { FlocaleFont *list = FlocaleFontList; int i = 0; if (!flf) { return; } /* Remove a weight, still too heavy? */ if (--(flf->count) > 0) { return; } if (flf->name != NULL && !StrEquals(flf->name, mb_fallback_font) && !StrEquals(flf->name, fallback_font)) { free(flf->name); } if (FftSupport && flf->fftf.fftfont != NULL) { FftFontClose(dpy, flf->fftf.fftfont); if (flf->fftf.fftfont_rotated_90 != NULL) FftFontClose(dpy, flf->fftf.fftfont_rotated_90); if (flf->fftf.fftfont_rotated_180 != NULL) FftFontClose(dpy, flf->fftf.fftfont_rotated_180); if (flf->fftf.fftfont_rotated_270 != NULL) FftFontClose(dpy, flf->fftf.fftfont_rotated_270); } if (flf->fontset != NULL) { XFreeFontSet(dpy, flf->fontset); } if (flf->font != NULL) { XFreeFont(dpy, flf->font); } if (flf->flags.must_free_fc) { if (flf->fc->x) free(flf->fc->x); if (flf->fc->bidi) free(flf->fc->bidi); if (flf->fc->locale != NULL) { while (FLC_GET_LOCALE_CHARSET(flf->fc,i) != NULL) { free(FLC_GET_LOCALE_CHARSET(flf->fc,i)); i++; } free(flf->fc->locale); } free(flf->fc); } /* Link it out of the list (it might not be there) */ if (flf == list) /* in head? simple */ { FlocaleFontList = flf->next; } else { while (list && list->next != flf) { /* fast forward until end or found */ list = list->next; } /* not end? means we found it in there, possibly at end */ if (list) { /* link around it */ list->next = flf->next; } } free(flf); } /* * Width and Drawing Text */ void FlocaleInitGstpArgs( flocale_gstp_args *args, FlocaleFont *flf, FlocaleWinString *fws, int start_x, int start_y) { args->step = 0; args->offset = flf->shadow_offset + 1; args->outer_offset = flf->shadow_offset + flf->shadow_size; args->size = flf->shadow_size; args->sdir = flf->flags.shadow_dir; switch (fws->flags.text_rotation) { case ROTATION_270: /* CCW */ args->orig_x = start_x + FLF_SHADOW_UPPER_SIZE(flf); args->orig_y = start_y + FLF_SHADOW_RIGHT_SIZE(flf); break; case ROTATION_180: args->orig_x = start_x + FLF_SHADOW_RIGHT_SIZE(flf); args->orig_y = start_y; break; case ROTATION_90: /* CW */ args->orig_x = start_x + FLF_SHADOW_BOTTOM_SIZE(flf); args->orig_y = start_y + FLF_SHADOW_LEFT_SIZE(flf); break; case ROTATION_0: default: args->orig_x = start_x + FLF_SHADOW_LEFT_SIZE(flf); args->orig_y = start_y; break; } args->rot = fws->flags.text_rotation; return; } Bool FlocaleGetShadowTextPosition( int *x, int *y, flocale_gstp_args *args) { if (args->step == 0) { args->direction = MULTI_DIR_NONE; args->inter_step = 0; } if ((args->step == 0 || args->inter_step >= args->num_inter_steps) && args->size != 0) { /* setup a new direction */ args->inter_step = 0; gravity_get_next_multi_dir(args->sdir, &args->direction); if (args->direction == MULTI_DIR_C) { int size; size = 2 * (args->outer_offset) + 1; args->num_inter_steps = size * size; } else { args->num_inter_steps = args->size; } } if (args->direction == MULTI_DIR_NONE || args->size == 0) { *x = args->orig_x; *y = args->orig_y; return False; } if (args->direction == MULTI_DIR_C) { int tx; int ty; int size; int is_finished; size = 2 * (args->outer_offset) + 1; tx = args->inter_step % size - args->outer_offset; ty = args->inter_step / size - args->outer_offset; for (is_finished = 0; ty <= args->outer_offset; ty++, tx = -args->outer_offset) { for (; tx <= args->outer_offset; tx++) { if (tx <= -args->offset || tx >= args->offset || ty <= -args->offset || ty >= args->offset) { is_finished = 1; break; } } if (is_finished) { break; } } args->inter_step = (tx + args->outer_offset) + (ty + args->outer_offset) * size; if (!is_finished) { tx = 0; ty = 0; } *x = args->orig_x + tx; *y = args->orig_y + ty; } else if (args->inter_step > 0) { /* into a directional drawing */ (*x) += args->x_sign; (*y) += args->y_sign; } else { direction_t dir; direction_t dir_x; direction_t dir_y; dir = gravity_multi_dir_to_dir(args->direction); gravity_split_xy_dir(&dir_x, &dir_y, dir); args->x_sign = gravity_dir_to_sign_one_axis(dir_x); args->y_sign = gravity_dir_to_sign_one_axis(dir_y); gravity_rotate_xy( args->rot, args->x_sign, args->y_sign, &args->x_sign, &args->y_sign); *x = args->orig_x + args->x_sign * args->offset; *y = args->orig_y + args->y_sign * args->offset; } args->inter_step++; args->step++; return True; } void FlocaleDrawString( Display *dpy, FlocaleFont *flf, FlocaleWinString *fws, unsigned long flags) { int len; Bool do_free = False; Pixel fg = 0, fgsh = 0; Bool has_fg_pixels = False; flocale_gstp_args gstp_args; superimpose_char_t *comb_chars = NULL; char *curr_str; int char_len; /* length in number of chars */ int *pixel_pos = NULL; int i; int j; char buf[4]; int curr_pixel_pos; int curr_len; if (!fws || !fws->str) { return; } if (flags & FWS_HAVE_LENGTH) { len = fws->len; } else { len = strlen(fws->str); } /* encode the string */ FlocaleEncodeWinString( dpy, flf, fws, &do_free, &len, &comb_chars, NULL); curr_str = fws->e_str; for(char_len = 0, i = 0 ; i < len && curr_str[i] != 0 ; char_len++, i += curr_len) { curr_len = FlocaleStringNumberOfBytes(flf, curr_str + i); } /* for superimposition calculate the character positions in pixels */ if (comb_chars != NULL && ( comb_chars[0].c.byte1 != 0 || comb_chars[0].c.byte2 != 0)) { /* the second condition is actually redundant, but there for clarity, ending at 0 is what's expected in a correct string */ pixel_pos = (int *)safemalloc( (char_len != 0 ? char_len : 1) * sizeof(int)); /* if there is 0 bytes in the encoded string, there might still be combining character to draw (at position 0) */ if(char_len == 0) { pixel_pos[0] = 0; } for( i = 0, curr_pixel_pos = 0 ; i < char_len ; i++, curr_str += curr_len) { curr_len = FlocaleStringNumberOfBytes(flf, curr_str); for (j = 0 ; j < curr_len ; j++) { buf[j] = curr_str[j]; } buf[j] = 0; pixel_pos[i] = curr_pixel_pos; /* need to compensate for shadow width (if any) */ curr_pixel_pos += FlocaleTextWidth(flf, buf, curr_len) - FLF_SHADOW_WIDTH(flf); } } /* get the pixels */ if (fws->flags.has_colorset) { fg = fws->colorset->fg; fgsh = fws->colorset->fgsh; has_fg_pixels = True; } else if (flf->shadow_size != 0) { XGCValues xgcv; if (XGetGCValues(dpy, fws->gc, GCForeground, &xgcv) != 0) { fg = xgcv.foreground; } else { fg = PictureBlackPixel(); } fgsh = GetShadow(fg); has_fg_pixels = True; } if(flf->font != None && (FLC_ENCODING_TYPE_IS_UTF_8(flf->fc) || flf->flags.is_mb)) { /* in this case, length is number of 2-byte chars */ len = char_len; } if (fws->flags.text_rotation != ROTATION_0 && flf->fftf.fftfont == NULL) { /* pass in information to perform superimposition */ FlocaleRotateDrawString( dpy, flf, fws, fg, fgsh, has_fg_pixels, len, comb_chars, pixel_pos); } else if (FftSupport && flf->fftf.fftfont != NULL) { FftDrawString( dpy, flf, fws, fg, fgsh, has_fg_pixels, len, flags); } else if (flf->fontset != None) { int xt = fws->x; int yt = fws->y; FlocaleInitGstpArgs(&gstp_args, flf, fws, fws->x, fws->y); if (flf->shadow_size != 0) { XSetForeground(dpy, fws->gc, fgsh); while (FlocaleGetShadowTextPosition( &xt, &yt, &gstp_args)) { XmbDrawString( dpy, fws->win, flf->fontset, fws->gc, xt, yt, fws->e_str, len); } } if (has_fg_pixels == True) { XSetForeground(dpy, fws->gc, fg); } xt = gstp_args.orig_x; yt = gstp_args.orig_y; XmbDrawString( dpy, fws->win, flf->fontset, fws->gc, xt, yt, fws->e_str, len); } else if (flf->font != None) { FlocaleFontStructDrawString( dpy, flf, fws->win, fws->gc, fws->x, fws->y, fg, fgsh, has_fg_pixels, fws, len, False); } /* here take care of superimposing chars */ i = 0; if (comb_chars != NULL) { while(comb_chars[i].c.byte1 != 0 && comb_chars[i].c.byte2 != 0) { /* draw composing character on top of corresponding "real" character */ FlocaleWinString tmp_fws = *fws; int offset = pixel_pos[comb_chars[i].position]; char *buf2; int out_len; curr_len = FlocaleChar2bOneCharToUtf8(comb_chars[i].c, buf); buf2 = FiconvUtf8ToCharset( dpy, flf->str_fc, (const char *)buf, curr_len); if(buf2 == NULL) { /* if conversion failed, combinational char is not representable in current charset */ /* just replace with empty string */ buf2 = (char *)safemalloc(sizeof(char)); buf2[0] = 0; } tmp_fws.e_str = buf2; tmp_fws.str2b = NULL; if(FftSupport && flf->fftf.fftfont != NULL) { tmp_fws.x = fws->x + offset; FftDrawString( dpy, flf, &tmp_fws, fg, fgsh, has_fg_pixels, strlen(buf2), flags); } else if(flf->fontset != None) { int xt = fws->x; int yt = fws->y; FlocaleInitGstpArgs( &gstp_args, flf, fws, fws->x, fws->y); if (flf->shadow_size != 0) { XSetForeground(dpy, fws->gc, fgsh); while (FlocaleGetShadowTextPosition( &xt, &yt, &gstp_args)) { XmbDrawString( dpy, fws->win, flf->fontset, fws->gc, xt, yt, buf2, strlen(buf2)); } } XSetForeground(dpy, fws->gc, fg); xt = gstp_args.orig_x; yt = gstp_args.orig_y; XmbDrawString( dpy, fws->win, flf->fontset, fws->gc, xt + offset, yt, buf2, strlen(buf2)); } else if (flf->font != None) { if (FLC_ENCODING_TYPE_IS_UTF_8(flf->fc)) { tmp_fws.str2b = (XChar2b *) safemalloc(2 * sizeof(XChar2b)); tmp_fws.str2b[0] = comb_chars[i].c; tmp_fws.str2b[1].byte1 = 0; tmp_fws.str2b[1].byte2 = 0; out_len = 1; /*tmp_fws.str2b = FlocaleUtf8ToUnicodeStr2b( tmp_fws.e_str, curr_len, &out_len);*/ } else if (flf->flags.is_mb) { tmp_fws.str2b = FlocaleStringToString2b( dpy, flf, tmp_fws.e_str, curr_len, &out_len); } else { out_len = strlen(buf2); } FlocaleFontStructDrawString( dpy, flf, fws->win, fws->gc, fws->x + offset, fws->y, fg, fgsh, has_fg_pixels, &tmp_fws, out_len, False); } free(buf2); if(tmp_fws.str2b != NULL) { free(tmp_fws.str2b); } i++; } } if (do_free) { if (fws->e_str != NULL) { free(fws->e_str); fws->e_str = NULL; } } if (fws->str2b != NULL) { free(fws->str2b); fws->str2b = NULL; } if(comb_chars != NULL) { free(comb_chars); if(pixel_pos) free(pixel_pos); } return; } void FlocaleDrawUnderline( Display *dpy, FlocaleFont *flf, FlocaleWinString *fws, int offset) { int off1, off2, y, x_s, x_e; superimpose_char_t *comb_chars = NULL; int *l_to_v = NULL; Bool do_free = True; int len = strlen(fws->str); int l_coffset; int v_coffset; int voffset; if (fws == NULL || fws->str == NULL) { return; } /* need to encode the string first to get BIDI and combining chars */ FlocaleEncodeWinString(dpy, flf, fws, &do_free, &len, &comb_chars, &l_to_v); /* we don't need this, only interested in char mapping */ free(comb_chars); /* now calculate char offset (in bytes) in visual string corresponding to coffset */ /* calculate absolute position in string (in characters) */ l_coffset = FlocaleStringByteToCharOffset(flf, fws->str, offset); /* map to an offset in the visual string */ v_coffset = l_to_v[l_coffset]; /* calculate byte offset into visual string */ voffset = FlocaleStringCharToByteOffset(flf, fws->e_str, v_coffset); off1 = FlocaleTextWidth(flf, fws->e_str, voffset) + ((voffset == 0)? FLF_SHADOW_LEFT_SIZE(flf) : - FLF_SHADOW_RIGHT_SIZE(flf) ); off2 = FlocaleTextWidth(flf, fws->e_str + voffset, FlocaleStringNumberOfBytes(flf, fws->e_str + voffset)) - FLF_SHADOW_WIDTH(flf) - 1 + off1; y = fws->y + 2; x_s = fws->x + off1; x_e = fws->x + off2; /* No shadow */ XDrawLine(dpy, fws->win, fws->gc, x_s, y, x_e, y); /* free encoded string if it isn't the same as input string */ if(fws->e_str != fws->str) { free(fws->e_str); fws->e_str = NULL; } if(fws->str2b != NULL) { free(fws->str2b); fws->str2b = NULL; } free(l_to_v); return; } int FlocaleTextWidth(FlocaleFont *flf, char *str, int sl) { int result = 0; char *tmp_str; int new_l,do_free; superimpose_char_t *comb_chars = NULL; if (!str || sl == 0) return 0; if (sl < 0) { /* a vertical string: nothing to do! */ sl = -sl; } /* FIXME */ /* to avoid eccesive calls iconv (slow in Solaris 8) don't bother to encode if string is one byte when drawing a string this function is used to calculate position of each character (for superimposition) */ if(sl == 1) { tmp_str = str; new_l = sl; do_free = False; } else { tmp_str = FlocaleEncodeString( Pdpy, flf, str, &do_free, sl, &new_l, NULL, &comb_chars, NULL); } /* if we get zero-length, check to to see if there if there's any combining chars, if so use an imagninary space as a "base character" */ if (strlen(tmp_str) == 0 && comb_chars && (comb_chars[0].c.byte1 != 0 || comb_chars[0].c.byte2 != 0)) { if(do_free) { free(tmp_str); } if(comb_chars) { free(comb_chars); } return FlocaleTextWidth(flf, " ", 1); } else if (FftSupport && flf->fftf.fftfont != NULL) { result = FftTextWidth(flf, tmp_str, new_l); } else if (flf->fontset != None) { result = XmbTextEscapement(flf->fontset, tmp_str, new_l); } else if (flf->font != None) { if (FLC_ENCODING_TYPE_IS_UTF_8(flf->fc) || flf->flags.is_mb) { XChar2b *str2b; int nl; if (FLC_ENCODING_TYPE_IS_UTF_8(flf->fc)) str2b = FlocaleUtf8ToUnicodeStr2b( tmp_str, new_l, &nl); else str2b = FlocaleStringToString2b( Pdpy, flf, tmp_str, new_l, &nl); if (str2b != NULL) { result = XTextWidth16(flf->font, str2b, nl); free(str2b); } } else { result = XTextWidth(flf->font, tmp_str, new_l); } } if (do_free) { free(tmp_str); } if (comb_chars) { free(comb_chars); } return result + ((result != 0)? FLF_SHADOW_WIDTH(flf):0); } int FlocaleGetMinOffset( FlocaleFont *flf, rotation_t rotation) { int min_offset; #ifdef FFT_BUGGY_FREETYPE switch(rotation) { case ROTATION_270: case ROTATION_180: /* better than descent */ min_offset = (flf->descent + flf->height - flf->ascent)/2; break; case ROTATION_0: case ROTATION_90: default: /* better than ascent */ min_offset = (flf->ascent + flf->height - flf->descent)/2; break; } #else switch(rotation) { case ROTATION_180: case ROTATION_90: /* better than descent */ min_offset = (flf->descent + flf->height - flf->ascent)/2; break; case ROTATION_270: case ROTATION_0: default: /* better than ascent */ min_offset = (flf->ascent + flf->height - flf->descent)/2; break; } #endif return min_offset; } void FlocaleAllocateWinString(FlocaleWinString **pfws) { *pfws = (FlocaleWinString *)safemalloc(sizeof(FlocaleWinString)); memset(*pfws, '\0', sizeof(FlocaleWinString)); } /* * Text properties */ void FlocaleGetNameProperty( Status (func)(Display *, Window, XTextProperty *), Display *dpy, Window w, FlocaleNameString *ret_name) { char **list; int num; XTextProperty text_prop; list = NULL; if (func(dpy, w, &text_prop) == 0) { return; } if (text_prop.encoding == XA_STRING) { /* STRING encoding, use this as it is */ ret_name->name = (char *)text_prop.value; ret_name->name_list = NULL; return; } /* not STRING encoding, try to convert XA_COMPOUND_TEXT */ if (XmbTextPropertyToTextList(dpy, &text_prop, &list, &num) >= Success && num > 0 && *list) { /* Does not consider the conversion is REALLY succeeded: * XmbTextPropertyToTextList return 0 (== Success) on success, * a negative int if it fails (and in this case we are not * here), the number of unconvertible char on "partial" * success*/ XFree(text_prop.value); /* return of XGetWM(Icon)Name() */ ret_name->name = *list; ret_name->name_list = list; } else { if (list) { XFreeStringList(list); } ret_name->name = (char *)text_prop.value; ret_name->name_list = NULL; } } void FlocaleFreeNameProperty(FlocaleNameString *ptext) { if (ptext->name_list != NULL) { if (ptext->name != NULL && ptext->name != *ptext->name_list) XFree(ptext->name); XFreeStringList(ptext->name_list); ptext->name_list = NULL; } else if (ptext->name != NULL) { XFree(ptext->name); } ptext->name = NULL; return; } Bool FlocaleTextListToTextProperty( Display *dpy, char **list, int count, XICCEncodingStyle style, XTextProperty *text_prop_return) { int ret = False; if (Flocale != NULL) { ret = XmbTextListToTextProperty( dpy, list, count, style, text_prop_return); if (ret == XNoMemory) { ret = False; } else { /* ret == Success or the number of unconvertible * characters. ret should be != XLocaleNotSupported * because in this case Flocale == NULL */ ret = True; } } if (!ret) { if (XStringListToTextProperty( list, count, text_prop_return) == 0) { ret = False; } else { ret = True; } } return ret; } /* * Info */ void FlocalePrintLocaleInfo(Display *dpy, int verbose) { FlocaleFont *flf = FlocaleFontList; int count = 0; FlocaleCharset *cs; fflush(stderr); fflush(stdout); fprintf(stderr,"fvwm info on locale:\n"); fprintf(stderr," locale: %s, Modifier: %s\n", (Flocale)? Flocale:"", (Fmodifiers)? Fmodifiers:""); cs = FlocaleCharsetGetDefaultCharset(dpy, NULL); fprintf(stderr," Default Charset: X: %s, Iconv: %s, Bidi: %s\n", cs->x, (cs->iconv_index >= 0)? cs->locale[cs->iconv_index]:"Not defined", (cs->bidi)? "Yes":"No"); FlocaleCharsetPrintXOMInfo(); while (flf) { count++; flf = flf->next; } fprintf(stderr," Number of loaded font: %i\n", count); if (verbose) { count = 0; flf = FlocaleFontList; while(flf) { cs = flf->fc; fprintf(stderr," * Font number %i\n", count); fprintf(stderr," fvwm info:\n"); fprintf(stderr," Name: %s\n", (flf->name)? flf->name:""); fprintf(stderr," Cache count: %i\n", flf->count); fprintf(stderr," Type: "); if (flf->font) { fprintf(stderr,"FontStruct\n"); } else if (flf->fontset) { fprintf(stderr,"FontSet\n"); } else { fprintf(stderr,"XftFont\n"); } fprintf(stderr, " Charset: X: %s, Iconv: %s, " "Bidi: %s\n", cs->x, (cs->iconv_index >= 0)? cs->locale[cs->iconv_index]:"Not defined", (cs->bidi)? "Yes":"No"); fprintf(stderr," height: %i, ascent: %i, " "descent: %i\n", flf->height, flf->ascent, flf->descent); fprintf(stderr," shadow size: %i, " "shadow offset: %i, shadow direction:%i\n", flf->shadow_size, flf->shadow_offset, flf->flags.shadow_dir); if (verbose >= 2) { if (flf->fftf.fftfont != NULL) { FftFontType *fftf; fftf = &flf->fftf; fprintf(stderr, " Xft info:\n" " - Vertical font:"); FftPrintPatternInfo( fftf->fftfont, False); fprintf(stderr, " " "- Rotated font 90:"); if (fftf->fftfont_rotated_90) FftPrintPatternInfo( fftf-> fftfont_rotated_90, True); else fprintf(stderr, " None\n"); fprintf(stderr, " " "- Rotated font 270:"); if (fftf->fftfont_rotated_270) FftPrintPatternInfo( fftf-> fftfont_rotated_270, True); else fprintf(stderr, " None\n"); fprintf(stderr, " " "- Rotated font 180:"); if (fftf->fftfont_rotated_180) FftPrintPatternInfo( fftf-> fftfont_rotated_180, True); else fprintf(stderr, " None\n"); } else if (flf->font != NULL) { char *full_name; full_name = FlocaleGetFullNameOfFontStruct( dpy, flf->font); fprintf(stderr, " X info:\n" " %s\n", (full_name)? full_name:"?"); if (full_name != NULL) { XFree(full_name); } } else if (flf->fontset != NULL) { int n,i; XFontStruct **font_struct_list; char **font_name_list; fprintf(stderr, " X info:\n"); n = XFontsOfFontSet( flf->fontset, &font_struct_list, &font_name_list); for(i = 0; i < n; i++) { fprintf(stderr, " %s\n", font_name_list[i]); } } } count++; flf = flf->next; } } }