/* * Copyright (C) 1997 and 1998 WIDE Project. All rights reserved. * Copyright (C) 2005 * National Institute of Advanced Industrial Science and Technology (AIST) * Registration Number H16PRO276 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $Id: m17n.c,v 1.8 2005/05/19 15:58:30 nishida Exp $ */ #ifdef USE_M17N #include "mgp.h" #ifdef HAVE_FONTCONFIG_FONTCONFIG_H /* This is to parse Xft font name pattern. */ #include #endif /* Prototypes for internal functions. They start with "M17N__". */ static int M17N__line_break __P ((MText *, int, int, int ,int, int)); static void M17N__format_line __P ((int, int, int *, int *)); static MText *M17N__gen_mtext __P ((u_char *)); static void M17N__define_font __P ((struct ctrl *)); static MFont *M17N__get_font __P ((char *, char *, int)); /* The following two variables are set by M17N_draw_string and referred by M17N__format_line (). */ /* Width remaining for the first line of a drawning text. */ static int first_width; /* Width of the currently drawing area. */ static int area_width; static MDrawControl control; static MFontset *fontset; static MFace *faces[2]; static MSymbol languages[2]; /* M-text containing a single space. It is used to get normal ascent/descent of an ASCII font selected for the current set of faces. */ static MText *space; static MSymbol Miso8859_1, Municode_bmp, Mjisx0208, Mgb2312, Mksc5601; static MSymbol Mlatin, Mhan, Mhangul, Mkatakana, Mhiragana; static int kinsoku_range [][2] = { {0x3041, 0x30FF}, /* Kana */ {0x2E80, 0x2FDF}, /* radical */ {0x3400, 0x4DB5}, /* CJK Ideograph Extention A */ {0x4E00, 0x9FAF}, /* CJK Ideograph */ {0XF900, 0xFAFF}, /* CJK COMPATIBILITY IDEOGRAPH */ {0X20000, 0x2A6D6}, /* CJK Ideograph Extention B */ {0x2F800, 0x2FA1D} /* CJK COMPATIBILITY IDEOGRAPH */ }; static int kinsoku_list [] = { /* UNICODE */ 0x2019, 0x201A, 0x201D, 0x201E, 0x2032, 0x2033, 0x2034, 0x203A, 0x203C, 0x203D, 0x2046, 0x2047, 0x2048, 0x2049, 0x204D, 0x3000, 0x3001, 0x3002, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017, 0x3018, 0x309B, 0x309C, 0x309D, 0x309E, 0x30FD, 0x30FD, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A, 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0xFF61, 0xFF63, 0xFF64, /* JISX208 */ 0x00A7, 0x00B0, 0x2018, 0x201C, 0x2032, 0x2033, 0x2103, 0x3008, 0x300A, 0x300C, 0x300E, 0x3010, 0x3014, 0x3016, 0xFF02, 0xFF08, 0xFF20, 0xFF3B, 0xFF5B }; static MCharTable *kinsoku_table; static MSymbol Mkinsoku; /* Find a linebreak position. Set in control.line_break */ static int M17N__line_break (mt, pos, from, to, line, y) MText *mt; int pos, from, to; int line, y; { int c, i = 0, opos = pos; c = mtext_ref_char(mt, pos); if (mchartable_lookup(kinsoku_table, c) == Mt) return pos; while(pos != from){ if (i ++ > 10) return opos; /* for stupidly long line */ pos --; c = mtext_ref_char(mt, pos); if (c < 256 && isspace(c)) return pos +1; if (mchartable_lookup(kinsoku_table, c) == Mt) return pos; } return pos; } /* Decide the width and indentation of the LINEth line. Set in control.format. */ static void M17N__format_line(line, y, indent, width) int line, y, *indent, *width; { *width = line == 0 ? first_width : area_width; *indent = 0; } /* Return an M-text generated from the byte sequence at P. */ static MText * M17N__gen_mtext (p) u_char *p; { MSymbol coding; MText *mt = 0; int i, len = strlen ((char *) p); if (len == 0) return NULL; if (!mt && mgp_charset[0] != '\0') { coding = mconv_resolve_coding (msymbol (mgp_charset)); if (coding) mt = mconv_decode_buffer (coding, p, len); } if (strchr ((char *) p, '\033')) { /* Try ISO-2022 encoding. */ coding = mconv_resolve_coding (msymbol ("iso-2022-7bit")); if (coding) mt = mconv_decode_buffer (coding, p, len); } if (!mt) { /* Check ASCII only. */ for (i = 0; i < len; i++) if (p[i] >= 0x80) break; if (i == len) mt = mtext_from_data (p, len, MTEXT_FORMAT_US_ASCII); } if (!mt) { /* Try UTF-8. */ coding = mconv_resolve_coding (msymbol ("utf-8")); if (coding) mt = mconv_decode_buffer (coding, p, len); } if (!mt){ /* Try the encoding of the current locale. */ mt = mconv_decode_buffer (Mnil, p, len); } if (!mt) { /* XXX: wrong encoding? */ fprintf(stderr, "unexpected byte sequence found.\n"); fprintf(stderr, " (expecting: %s (by %%charset), iso-2022-7bit, utf-8, default encoding of locale)\n", mgp_charset); exit(1); } return mt; } /* Return a font matching SEED and REGISTRY. If no such font is found and RECORD is nonzero, create a new font and remember it. */ MFont * M17N__get_font (seed, registry, record) char *seed; char *registry; int record; { static MPlist *font_list; MPlist *plist; MSymbol sym_seed, sym_registry; MSymbol family, weight, slant; MFont *font; char *p; if (! font_list) font_list = mplist (); sym_seed = msymbol (seed); if (strncmp (registry, "iso8859", 7) == 0) sym_registry = msymbol (registry); else if (strncmp (registry, "jisx0208", 8) == 0) sym_registry = Mjisx0208; else if (strncmp (registry, "gb2312", 6) == 0) sym_registry = Mgb2312; else if (strncmp (registry, "ksc5601", 7) == 0) sym_registry = Mksc5601; else sym_registry = msymbol (registry); for (plist = mplist_find_by_key (font_list, sym_seed); plist; plist = mplist_find_by_key (plist, sym_seed)) { font = mplist_value (plist); if (mfont_get_prop (font, Mregistry) == sym_registry) return font; } if (! record) return NULL; family = weight = slant = Mnil; if ((p = strchr (seed, '-')) != NULL) { /* Should be XLFD of format FAMILY-WEIGHT[-SLANT] */ char *copy = alloca (strlen (seed) + 1); strcpy (copy, seed); copy[p - seed] = '\0'; family = msymbol (copy); copy += p + 1 - seed; p = strchr (copy, '-'); if (! p) weight = msymbol (copy); else { *p = 0; weight = msymbol (copy); slant = msymbol (p + 1); } } else if (strchr (seed, ':')) { /* Should be Xft (i.e. Fontconfig) pattern. */ #ifdef HAVE_FONTCONFIG_FONTCONFIG_H FcPattern *pat = FcNameParse ((FcChar8 *) seed); FcChar8 *p; int i; if (pat) { if (FcPatternGetString (pat, FC_FAMILY, 0, &p) == FcResultMatch) family = msymbol ((char *) p); if (FcPatternGetInteger (pat, FC_WEIGHT, 0, &i) == FcResultMatch) weight = (i < FC_WEIGHT_EXTRALIGHT ? msymbol ("ultralight") : i < FC_WEIGHT_LIGHT ? msymbol ("extralight") : i < FC_WEIGHT_REGULAR ? msymbol ("light") : i < FC_WEIGHT_MEDIUM ? msymbol ("regular") : i < FC_WEIGHT_DEMIBOLD ? msymbol ("medium") : i < FC_WEIGHT_BOLD ? msymbol ("demibold") : i < FC_WEIGHT_EXTRABOLD ? msymbol ("bold") : i < FC_WEIGHT_ULTRABOLD ? msymbol ("extrabold") : i < FC_WEIGHT_BLACK ? msymbol ("ultrabold") : msymbol ("black")); if (FcPatternGetInteger (pat, FC_SLANT, 0, &i) == FcResultMatch) slant = (i < FC_SLANT_ITALIC ? msymbol ("r") : i < FC_SLANT_OBLIQUE ? msymbol ("i") : msymbol ("o")); FcPatternDestroy (pat); } else #endif /* Treat the seed as family name. */ family = msymbol (seed); } else /* Treat the name as family name. */ family = msymbol (seed); font = mfont (); mfont_put_prop (font, Mfamily, family); mfont_put_prop (font, Mweight, weight); mfont_put_prop (font, Mstyle, slant); mfont_put_prop (font, Mregistry, sym_registry); mplist_push (font_list, sym_seed, font); return font; } /* Pre-define the font set in CP. */ void M17N__define_font (cp) struct ctrl *cp; { MSymbol family, registry; MPlist *plist, *pl; MFont *font, *recorded; for (; cp && cp->ct_op != CTL_XFONT2; cp = cp->ct_next); if (! cp) return; recorded = M17N__get_font (cp->ctc2_value1, cp->ctc2_value2, 1); family = mfont_get_prop (recorded, Mfamily); registry = mfont_get_prop (recorded, Mregistry); if (registry == Miso8859_1) { /* This may be a TTF, in which case, we may be able to use it for any scripts. */ plist = mfontset_lookup (fontset, Mt, Mnil, Mnil); } else { plist = mplist (); if (registry == Mjisx0208 || registry == Mgb2312) mplist_put (plist, Mhan, NULL); else if (registry == Mksc5601) mplist_put (plist, Mhangul, NULL); else if (strncmp (msymbol_name (registry), "iso8859-", 8) == 0) mplist_put (plist, Mlatin, NULL); } font = mfont (); mfont_put_prop (font, Mfamily, family); for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl)) { MSymbol script = mplist_key (pl); MPlist *p = mfontset_lookup (fontset, script, Mt, Mnil); MPlist *p0; for (p0 = p; mplist_key (p0) != Mnil; p0 = mplist_next (p0)) { MSymbol lang = mplist_key (p0); mfont_put_prop (font, Mregistry, registry); mfontset_modify_entry (fontset, script, lang, Mnil, font, Mnil, -1); if (registry == Miso8859_1) { mfont_put_prop (font, Mregistry, Municode_bmp); mfontset_modify_entry (fontset, script, lang, Mnil, font, Mnil, -1); } } m17n_object_unref (p); } m17n_object_unref (plist); mfont_put_prop (font, Mregistry, registry); mfontset_modify_entry (fontset, Mnil, Mnil, Mnil, font, Mnil, -1); if (registry == Miso8859_1) { mfont_put_prop (font, Mregistry, Municode_bmp); mfontset_modify_entry (fontset, Mnil, Mnil, Mnil, font, Mnil, -1); } } /* Internal API (functions called from the other files) */ void M17N_init() { int i; M17N_INIT(); space = mtext_from_data (" ", 1, MTEXT_FORMAT_US_ASCII); fontset = mfontset ("default"); memset (&control, 0, sizeof control); control.two_dimensional = 1; control.enable_bidi = 1; control.ignore_formatting_char = 1; control.disable_caching = 0; control.max_line_width = 0; control.anti_alias = 1; control.format = M17N__format_line; control.line_break = M17N__line_break; /* Generate kinsoku_char_table. */ Mkinsoku = msymbol ("kinsoku"); kinsoku_table = mchartable (Msymbol, Mnil); for (i = 0; i < (sizeof(kinsoku_range) / sizeof(kinsoku_range[0])); i++) mchartable_set_range (kinsoku_table, kinsoku_range[i][0], kinsoku_range[i][1], Mt); for (i = 0; i < (sizeof kinsoku_list / sizeof (int)); i++) mchartable_set (kinsoku_table, kinsoku_list[i], Mkinsoku); Miso8859_1 = msymbol ("iso8859-1"); Municode_bmp = msymbol ("unicode-bmp"); Mjisx0208 = msymbol ("jisx0208.1983-0"); Mgb2312 = msymbol ("gb2312.1980-0"); Mksc5601 = msymbol ("ksc5601.1987-0"); Mlatin = msymbol ("latin"); Mhan = msymbol ("han"); Mhangul = msymbol ("hangul"); Mkatakana = msymbol ("katakana"); Mhiragana = msymbol ("hiragana"); for (i = 0; i < MAXFONTDEF; i++) if (fontdef_control[i]) M17N__define_font (fontdef_control[i]); } /* Set font-oriented properties (family, weight, slant) of the current face to what specified in SEED. */ void M17N_set_font (seed, registry) char *seed; char *registry; { MFont *font = M17N__get_font (seed, registry, 0); MFace *face = faces[caching]; if (! font) return; if (! faces[caching]) face = faces[caching] = mface (); mface_put_prop (face, Mfamily, (MSymbol) mfont_get_prop (font, Mfamily)); mface_put_prop (face, Mweight, mfont_get_prop (font, Mweight)); mface_put_prop (face, Mstyle, mfont_get_prop (font, Mstyle)); } void M17N_set_color (color) u_long color; { MFace *face = faces[caching]; char color_string[20]; XColor xcolor; if (! faces[caching]) face = faces[caching] = mface (); xcolor.pixel = color; XQueryColor (display, colormap, &xcolor); sprintf (color_string, "#%04X%04X%04X", xcolor.red, xcolor.green, xcolor.blue); mface_put_prop (face, Mforeground, msymbol (color_string)); } /* Calculate the extents of text pointed by CP and update STATE. */ void M17N_draw_string (state, cp) struct render_state *state; struct ctrl *cp; { MText *mt; static MFrame *drawframe; static MPlist *plist; MDrawGlyphInfo info; MDrawMetric rect_sp, rect; int ascent = 0, descent = 0; int nchars; int i, c; if (cp->ct_flag == 0) { /* cp->ctc_value points a raw byte sequence. */ mt = M17N__gen_mtext ((u_char *) cp->ctc_value); cp->ctc_value = (char *) mt; cp->ct_flag = 1; } else { /* cp->ctc_valus points an M-text. */ mt = (MText *) cp->ctc_value; } if (! mt) return; nchars = mtext_len (mt); if (! plist) { plist = mplist (); mplist_add (plist, Mdisplay, display); drawframe = mframe (plist); } if (! mtext_get_prop (mt, 0, Mface)) { MFace *face; if (! faces[caching]) faces[caching] = mface (); face = mface_copy (faces[caching]); mface_put_prop (face, Msize, (void *) (char_size[caching] * 10)); mtext_push_prop (mt, 0, nchars, Mface, face); if (languages[caching]) mtext_push_prop (mt, 0, nchars, Mlanguage, languages[caching]); m17n_object_unref (face); } /* Compare the normal ascent/descent and the physical ascent/descent of this M-text, and use the bigger ones. */ mdraw_text_extents(drawframe, space, 0, 1, &control, NULL, NULL, &rect_sp); rect_sp.height += rect_sp.y; /* calculate descent */ area_width = state->width - state->leftfillpos / 2; first_width = area_width - state->linewidth; mdraw_glyph_info (drawframe, mt, 0, 0, &control, &info); if (info.line_to == nchars) { /* All text can be drawn on the current line. */ mdraw_text_extents (drawframe, mt, 0, nchars, &control, NULL, NULL, &rect); rect.height += rect.y; /* calculate descent */ ascent = rect_sp.y < rect.y ? - rect_sp.y : - rect.y; descent = rect_sp.height > rect.height ? rect_sp.height : rect.height; for (i = 0; state->brankline && i < nchars; i++) if ((c = mtext_ref_char (mt, i)) != ' ' && c != '\t') state->brankline = 0; draw_line_itemsize(state, ascent, descent, 0); obj_new_mtext (state, state->linewidth, state->charoff, mt, 0, nchars, drawframe, ascent, descent); state->linewidth += rect.width; } else { int i, c; MDrawGlyphInfo info2; /* We need a line break. Check if there's a room for at least one fragment on the current line. */ /* Skip the leading spaces. */ for (i = 0; i < nchars && mtext_ref_char (mt, 0) == ' '; i++); for (; i < nchars; i++) { if ((c = mtext_ref_char (mt, i)) == ' ') break; else if (mchartable_lookup (kinsoku_table, c) == Mt) { i++; break; } } mdraw_glyph_info (drawframe, mt, 0, i - 1, &control, &info2); if (info2.line_from > 0) { /* No room even for one fragment. Break the line before drawing anything. */ draw_line_end (state); draw_line_start (state); state->linewidth = state->leftfillpos; first_width = area_width; mdraw_clear_cache (mt); mdraw_glyph_info (drawframe, mt, 0, 0, &control, &info); } while (1) { for (i = info.line_to; i > info.line_from; i--) if ((c = mtext_ref_char (mt, i - 1)) != ' ' && c != '\t') { state->brankline = 0; break; } if (i == info.line_from) i = info.line_to; mdraw_text_extents (drawframe, mt, info.line_from, i, &control, NULL, NULL, &rect); rect.height += rect.y; /* calculate descent */ ascent = rect_sp.y < rect.y ? - rect_sp.y : - rect.y; descent = rect_sp.height > rect.height ? rect_sp.height : rect.height; draw_line_itemsize (state, ascent, descent, 0); obj_new_mtext (state, state->linewidth, state->charoff, mt, info.line_from, i, drawframe, ascent, descent); state->linewidth += rect.width; if (info.line_to >= nchars) break; draw_line_end (state); draw_line_start (state); state->linewidth = state->leftfillpos; mdraw_glyph_info (drawframe, mt, info.line_to, info.line_to, &control, &info); } } } /* Draw the M-text stored in OBJ on TARGET at the coordinate (X Y). */ void M17N_draw_object(obj, target, x, y) struct render_object *obj; Drawable target; int x, y; { control.format = NULL; mdraw_text_with_control ( obj->data.m17ntext.drawframe, (MDrawWindow) target, x, y, obj->data.m17ntext.mt, obj->data.m17ntext.from, obj->data.m17ntext.to, &control); control.format = M17N__format_line; } /* Update faces[caching] or languages[caching] according to KEY and VALUE. */ void M17N_process_direc(key, value) char *key, *value; { MSymbol key_sym = msymbol (key); MFace *face = faces[caching]; if (! faces[caching]) face = faces[caching] = mface (); if (key_sym == Mfoundry || key_sym == Mfamily || key_sym == Mweight || key_sym == Mstyle || key_sym == Mstretch || key_sym == Madstyle) mface_put_prop (face, key_sym, value ? msymbol (value) : Mnil); else if (key_sym == Mfontset) mface_put_prop (face, key_sym, value ? mfontset (value) : fontset); else if (key_sym == Mratio) mface_put_prop (face, key_sym, value ? (void *) atoi (value) : (void *) 1); else if (key_sym == Mlanguage) languages[caching] = value ? msymbol (value) : Mnil; } #endif