# HG changeset patch # User Kenichi Handa # Date 1288313413 -32400 # Node ID 24c75a5f002aa7679b0c4d8d8528562015e25726 # Parent 0576327092abd82a9d0bede80529c3ed45878942 Implement various display methods for glyphless characters. diff -r 0576327092ab -r 24c75a5f002a lisp/ChangeLog --- a/lisp/ChangeLog Thu Oct 28 19:57:59 2010 +0900 +++ b/lisp/ChangeLog Fri Oct 29 09:50:13 2010 +0900 @@ -1,3 +1,13 @@ +2010-10-28 Kenichi Handa + + Implement various display methods for glyphless characters. + + * international/characters.el (char-acronym-table): New variable. + (glyphless-char-control): New variable. + (update-glyphless-char-display): New funciton. + + * faces.el (glyphless-char): New face. + 2010-10-28 Glenn Morris * term/ns-win.el (global-map, menu-bar-final-items, menu-bar-help-menu): diff -r 0576327092ab -r 24c75a5f002a lisp/faces.el --- a/lisp/faces.el Thu Oct 28 19:57:59 2010 +0900 +++ b/lisp/faces.el Fri Oct 29 09:50:13 2010 +0900 @@ -2482,6 +2482,12 @@ (defface help-argument-name '((((supports :slant italic)) :inherit italic)) "Face to highlight argument names in *Help* buffers." :group 'help) + +(defface glyphless-char '((t :height 0.6)) + "Face for displaying non-graphic characters (e.g. U+202A (LRE)). +It is used for characters of no fonts too." + :version "24.1" + :group 'basic-faces) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Manipulating font names. diff -r 0576327092ab -r 24c75a5f002a lisp/international/characters.el --- a/lisp/international/characters.el Thu Oct 28 19:57:59 2010 +0900 +++ b/lisp/international/characters.el Fri Oct 29 09:50:13 2010 +0900 @@ -1234,6 +1234,131 @@ (optimize-char-table (standard-category-table)) +;; Display of glyphless characters. + +(defvar char-acronym-table + (make-char-table 'char-acronym-table nil) + "Char table of acronyms for non-graphic characters.") + +(let ((c0-acronyms '("NUL" "SOH" "STX" "ETX" "EOT" "ENQ" "ACK" "BEL" + "BS" nil nil "VT" "FF" "CR" "SO" "SI" + "DLE" "DC1" "DC2" "DC3" "DC4" "NAK" "SYN" "ETB" + "CAN" "EM" "SUB" "ESC" "FC" "GS" "RS" "US"))) + (dotimes (i 32) + (aset char-acronym-table i (car c0-acronyms)) + (setq c0-acronyms (cdr c0-acronyms)))) + +(let ((c1-acronyms '("XXX" "XXX" "BPH" "NBH" "IND" "NEL" "SSA" "ESA" + "HTS" "HTJ" "VTS" "PLD" "PLU" "R1" "SS2" "SS1" + "DCS" "PU1" "PU2" "STS" "CCH" "MW" "SPA" "EPA" + "SOS" "XXX" "SC1" "CSI" "ST" "OSC" "PM" "APC"))) + (dotimes (i 32) + (aset char-acronym-table (+ #x0080 i) (car c1-acronyms)) + (setq c1-acronyms (cdr c1-acronyms)))) + +(aset char-acronym-table #x17B4 "KIVAQ") ; KHMER VOWEL INHERENT AQ +(aset char-acronym-table #x17B5 "KIVAA") ; KHMER VOWEL INHERENT AA +(aset char-acronym-table #x200B "ZWSP") ; ZERO WIDTH SPACE +(aset char-acronym-table #x200C "ZWNJ") ; ZERO WIDTH NON-JOINER +(aset char-acronym-table #x200D "ZWJ") ; ZERO WIDTH JOINER +(aset char-acronym-table #x200E "LRM") ; LEFT-TO-RIGHT MARK +(aset char-acronym-table #x200F "RLM") ; RIGHT-TO-LEFT MARK +(aset char-acronym-table #x202A "LRE") ; LEFT-TO-RIGHT EMBEDDING +(aset char-acronym-table #x202B "RLE") ; RIGHT-TO-LEFT EMBEDDING +(aset char-acronym-table #x202C "PDF") ; POP DIRECTIONAL FORMATTING +(aset char-acronym-table #x202D "LRO") ; LEFT-TO-RIGHT OVERRIDE +(aset char-acronym-table #x202E "RLO") ; RIGHT-TO-LEFT OVERRIDE +(aset char-acronym-table #x2060 "WJ") ; WORD JOINER +(aset char-acronym-table #x206A "ISS") ; INHIBIT SYMMETRIC SWAPPING +(aset char-acronym-table #x206B "ASS") ; ACTIVATE SYMMETRIC SWAPPING +(aset char-acronym-table #x206C "IAFS") ; INHIBIT ARABIC FORM SHAPING +(aset char-acronym-table #x206D "AAFS") ; ACTIVATE ARABIC FORM SHAPING +(aset char-acronym-table #x206E "NADS") ; NATIONAL DIGIT SHAPES +(aset char-acronym-table #x206F "NODS") ; NOMINAL DIGIT SHAPES +(aset char-acronym-table #xFEFF "ZWNBSP") ; ZERO WIDTH NO-BREAK SPACE +(aset char-acronym-table #xFFF9 "IAA") ; INTERLINEAR ANNOTATION ANCHOR +(aset char-acronym-table #xFFFA "IAS") ; INTERLINEAR ANNOTATION SEPARATOR +(aset char-acronym-table #xFFFB "IAT") ; INTERLINEAR ANNOTATION TERMINATOR +(aset char-acronym-table #x1D173 "BEGBM") ; MUSICAL SYMBOL BEGIN BEAM +(aset char-acronym-table #x1D174 "ENDBM") ; MUSICAL SYMBOL END BEAM +(aset char-acronym-table #x1D175 "BEGTIE") ; MUSICAL SYMBOL BEGIN TIE +(aset char-acronym-table #x1D176 "END") ; MUSICAL SYMBOL END TIE +(aset char-acronym-table #x1D177 "BEGSLR") ; MUSICAL SYMBOL BEGIN SLUR +(aset char-acronym-table #x1D178 "ENDSLR") ; MUSICAL SYMBOL END SLUR +(aset char-acronym-table #x1D179 "BEGPHR") ; MUSICAL SYMBOL BEGIN PHRASE +(aset char-acronym-table #x1D17A "ENDPHR") ; MUSICAL SYMBOL END PHRASE +(aset char-acronym-table #xE0001 "|->TAG") ; LANGUAGE TAG +(aset char-acronym-table #xE0020 "SP TAG") ; TAG SPACE +(dotimes (i 94) + (aset char-acronym-table (+ #xE0021 i) (format " %c TAG" (+ 33 i)))) +(aset char-acronym-table #xE007F "->|TAG") ; CANCEL TAG + +;;; Control of displaying glyphless characters. +(defvar glyphless-char-control + '((format-control . thin-space) + (no-font . hexa-code)) + "List of directives to control displaying of glyphless characters. + +Each element has the form (TARGET . METHOD), where TARGET is a +symbol specifying the target character group to control, and +METHOD is a symbol specifying the method of displaying them. + +TARGET must be one of these symbols: + `c0-control': U+0000..U+001F. + `c1-control': U+0080..U+009F. + `format-control': Characters of Unicode General Category `Cf'. + Ex: U+200C (ZWNJ), U+200E (LRM)), but don't include characters + that have graphic image such as U+00AD (SHY). + `no-font': characters for which no suitable font is found. + +METHOD must be one of these symbols: + `zero-width': don't display. + `thin-space': display a thin space (1-pixel width). + `empty-box': display an empty box. + `acronym': display an acronum string in a box. + `hexa-code': display a hexadecimal character code in a box. + +Just setting this variable does not take effect. Call the +function `update-glyphless-char-display' (which see) after +setting this variable.") + +(defun update-glyphless-char-display () + "Make the setting of `glyphless-char-control' take effect. +This function updates the char-table `glyphless-char-display'." + (dolist (elt glyphless-char-control) + (let ((target (car elt)) + (method (cdr elt))) + (cond ((eq target 'c0-control) + (set-char-table-range glyphless-char-display '(#x00 . #x1F) + method)) + ((eq target 'c1-control) + (set-char-table-range glyphless-char-display '(#x80 . #x9F) + method)) + ((eq target 'format-control) + (map-char-table + #'(lambda (char category) + (if (eq category 'Cf) + (let ((this-method method) + from to) + (if (consp char) + (setq from (car char) to (cdr char)) + (setq from char to char)) + (while (<= from to) + (when (/= from #xAD) + (if (eq method 'acronym) + (setq this-method + (aref char-acronym-table from))) + (set-char-table-range glyphless-char-display + from this-method)) + (setq from (1+ from)))))) + unicode-category-table)) + ((eq target 'no-font) + (set-char-table-extra-slot glyphless-char-display 0 method)) + (t + (error "Invalid target character group: %s" target)))))) + +(update-glyphless-char-display) + ;;; Setting word boundary. (setq word-combining-categories diff -r 0576327092ab -r 24c75a5f002a src/ChangeLog --- a/src/ChangeLog Thu Oct 28 19:57:59 2010 +0900 +++ b/src/ChangeLog Fri Oct 29 09:50:13 2010 +0900 @@ -1,3 +1,46 @@ +2010-10-28 Kenichi Handa + + Implement various display methods for glyphless characters. + + * xdisp.c (Qglyphless_char, Vglyphless_char_display) + (Qglyphless_char_display, Qhexa_code, Qempty_box, Qthin_space) + (Qzero_width): New variables. + (THIN_SPACE_WIDTH): New macro. + (lookup_glyphless_char_display): New funciton. + (last_glyphless_glyph_frame, last_glyphless_glyph_face_id) + (last_glyphless_glyph_merged_face_id): New variables. + (get_next_display_element): Check glyphless characters. + (redisplay_internal): Initialize last_glyphless_glyph_frame and + last_glyphless_glyph_face_id. + (fill_glyphless_glyph_string): New function. + (BUILD_GLYPHLESS_GLYPH_STRING): New macro. + (BUILD_GLYPH_STRINGS): Handle the case GLYPHLESS_GLYPH. + (append_glyphless_glyph, produce_glyphless_glyph): New functions. + (x_produce_glyphs): If a suitable font is not found, produce a + glyphless glyph. Handle the case it->what == IT_GLYPHLESS. + (syms_of_xdisp): Intern and staticpro Qglyphless_char, + Qglyphless_char_display, Qhexa_code, Qempty_box, Qthin_space, and + Qzero_width. + (Vglyphless_char_display): Declare it as a Lisp variable. + + * dispextern.h (enum glyph_type): Add GLYPHLESS_GLYPH. + (struct glyph): Change the size of the member "type" to 3. Add + glyphless to the union slice and u. + (enum display_element_type): Add IT_GLYPHLESS. + (enum glyphless_display_method): New enum. + (struct it): New member glyphless_method. + (Vglyphless_char_display): Extern it. + + * xterm.c (x_draw_glyphless_glyph_string_foreground): New function. + (x_draw_glyph_string): Handle the case GLYPHLESS_GLYPH. + + * w32term.c (x_draw_glyphless_glyph_string_foreground): New + function. + (x_draw_glyph_string): Handle the case GLYPHLESS_GLYPH. + + * nsterm.m (ns_draw_glyph_string): Handle the case + GLYPHLESS_GLYPH (the detail is not yet implemented). + 2010-10-26 Juanma Barranquero * eval.c (init_eval_once): Set max_lisp_eval_depth to 600; diff -r 0576327092ab -r 24c75a5f002a src/dispextern.h --- a/src/dispextern.h Thu Oct 28 19:57:59 2010 +0900 +++ b/src/dispextern.h Fri Oct 29 09:50:13 2010 +0900 @@ -279,6 +279,9 @@ /* Glyph describes a static composition. */ COMPOSITE_GLYPH, + /* Glyph describes a glyphless character. */ + GLYPHLESS_GLYPH, + /* Glyph describes an image. */ IMAGE_GLYPH, @@ -333,7 +336,7 @@ /* Which kind of glyph this is---character, image etc. Value should be an enumerator of type enum glyph_type. */ - unsigned type : 2; + unsigned type : 3; /* 1 means this glyph was produced from multibyte text. Zero means it was produced from unibyte text, i.e. charsets aren't @@ -402,6 +405,11 @@ /* Start and end indices of glyphs of a graphme cluster of a composition (type == COMPOSITE_GLYPH). */ struct { int from, to; } cmp; + /* Pixel offsets for upper and lower part of the acronym. */ + struct { + short upper_xoff, upper_yoff; + short lower_xoff, lower_yoff; + } glyphless; } slice; /* A union of sub-structures for different glyph types. */ @@ -433,6 +441,19 @@ } stretch; + /* Sub-stretch for type == GLYPHLESS_GLYPH. */ + struct + { + /* Value is an enum of the type glyphless_display_method. */ + unsigned method : 2; + /* 1 iff this glyph is for a character of no font. */ + unsigned for_no_font : 1; + /* Length of acronym or hexadecimal code string (at most 8). */ + unsigned len : 4; + /* Character to display. Actually we need only 22 bits. */ + unsigned ch : 26; + } glyphless; + /* Used to compare all bit-fields above in one step. */ unsigned val; } u; @@ -1918,6 +1939,9 @@ /* A composition (static and automatic). */ IT_COMPOSITION, + /* A glyphless character (e.g. ZWNJ, LRE). */ + IT_GLYPHLESS, + /* An image. */ IT_IMAGE, @@ -1964,6 +1988,20 @@ WINDOW_WRAP }; +/* An enumerator for the method of displaying glyphless characters. */ + +enum glyphless_display_method + { + /* Display a thin (1-pixel width) space. */ + GLYPHLESS_DISPLAY_THIN_SPACE, + /* Display an empty box of proper width. */ + GLYPHLESS_DISPLAY_EMPTY_BOX, + /* Display an acronym string in a box. */ + GLYPHLESS_DISPLAY_ACRONYM, + /* Display a hexadecimal character code in a box. */ + GLYPHLESS_DISPLAY_HEXA_CODE + }; + struct it_slice { Lisp_Object x; @@ -2295,6 +2333,10 @@ PRODUCE_GLYPHS, this should be set beforehand too. */ int char_to_display; + /* If what == IT_GLYPHLESS, the method to display such a + character. */ + enum glyphless_display_method glyphless_method; + /* If what == IT_IMAGE, the id of the image to display. */ int image_id; @@ -2976,6 +3018,7 @@ extern Lisp_Object Vmouse_autoselect_window; extern int unibyte_display_via_language_environment; extern EMACS_INT underline_minimum_offset; +extern Lisp_Object Vglyphless_char_display; extern void reseat_at_previous_visible_line_start (struct it *); diff -r 0576327092ab -r 24c75a5f002a src/nsterm.m --- a/src/nsterm.m Thu Oct 28 19:57:59 2010 +0900 +++ b/src/nsterm.m Fri Oct 29 09:50:13 2010 +0900 @@ -2983,6 +2983,22 @@ ns_unfocus (s->f); break; + case GLYPHLESS_GLYPH: + n = ns_get_glyph_string_clip_rect (s, r); + ns_focus (s->f, r, n); + + if (s->for_overlaps || (s->cmp_from > 0 + && ! s->first_glyph->u.cmp.automatic)) + s->background_filled_p = 1; + else + ns_maybe_dumpglyphs_background + (s, s->first_glyph->type == COMPOSITE_GLYPH); + /* ... */ + /* Not yet implemented. */ + /* ... */ + ns_unfocus (s->f); + break; + default: abort (); } diff -r 0576327092ab -r 24c75a5f002a src/w32term.c --- a/src/w32term.c Thu Oct 28 19:57:59 2010 +0900 +++ b/src/w32term.c Fri Oct 29 09:50:13 2010 +0900 @@ -1394,6 +1394,92 @@ } +/* Draw the foreground of glyph string S for glyphless characters. */ + +static void +x_draw_glyphless_glyph_string_foreground (struct glyph_string *s) +{ + struct glyph *glyph = s->first_glyph; + XChar2b char2b[8]; + int x, i, j; + + /* If first glyph of S has a left box line, start drawing the text + of S to the right of that box line. */ + if (s->face->box != FACE_NO_BOX + && s->first_glyph->left_box_line_p) + x = s->x + eabs (s->face->box_line_width); + else + x = s->x; + + SetTextColor (s->hdc, s->gc->foreground); + SetBkColor (s->hdc, s->gc->background); + SetTextAlign (s->hdc, TA_BASELINE | TA_LEFT); + + s->char2b = char2b; + + for (i = 0; i < s->nchars; i++, glyph++) + { + char buf[7], *str = NULL; + int len = glyph->u.glyphless.len; + + if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM) + { + if (len > 1 + && CHAR_TABLE_P (Vglyphless_char_display) + && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display)) + >= 1)) + { + Lisp_Object acronym + = (! glyph->u.glyphless.for_no_font + ? CHAR_TABLE_REF (Vglyphless_char_display, + glyph->u.glyphless.ch) + : XCHAR_TABLE (Vglyphless_char_display)->extras[0]); + if (STRINGP (acronym)) + str = (char *) SDATA (acronym); + } + } + else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEXA_CODE) + { + sprintf ((char *) buf, "%0*X", + glyph->u.glyphless.ch < 0x10000 ? 4 : 6, + glyph->u.glyphless.ch); + str = buf; + } + + if (str) + { + struct font *font = s->font; + int upper_len = (len + 1) / 2; + unsigned code; + HFONT old_font; + + old_font = SelectObject (s->hdc, FONT_HANDLE (font)); + /* It is assured that all LEN characters in STR is ASCII. */ + for (j = 0; j < len; j++) + { + code = font->driver->encode_char (font, str[j]); + STORE_XCHAR2B (char2b + j, code >> 8, code & 0xFF); + } + font->driver->draw (s, 0, upper_len, + x + glyph->slice.glyphless.upper_xoff, + s->ybase + glyph->slice.glyphless.upper_yoff, + 0); + font->driver->draw (s, upper_len, len, + x + glyph->slice.glyphless.lower_xoff, + s->ybase + glyph->slice.glyphless.lower_yoff, + 0); + SelectObject (s->hdc, old_font); + } + if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE) + w32_draw_rectangle (s->hdc, s->gc, + x, s->ybase - glyph->ascent, + glyph->pixel_width - 1, + glyph->ascent + glyph->descent - 1); + x += glyph->pixel_width; + } +} + + /* Brightness beyond which a color won't have its highlight brightness boosted. @@ -2282,6 +2368,15 @@ x_draw_composite_glyph_string_foreground (s); break; + case GLYPHLESS_GLYPH: + if (s->for_overlaps || (s->cmp_from > 0 + && ! s->first_glyph->u.cmp.automatic)) + s->background_filled_p = 1; + else + x_draw_glyph_string_background (s, 1); + x_draw_glyphless_glyph_string_foreground (s); + break; + default: abort (); } diff -r 0576327092ab -r 24c75a5f002a src/xdisp.c --- a/src/xdisp.c Thu Oct 28 19:57:59 2010 +0900 +++ b/src/xdisp.c Fri Oct 29 09:50:13 2010 +0900 @@ -932,6 +932,21 @@ /* Number of seconds to wait before displaying an hourglass cursor. */ Lisp_Object Vhourglass_delay; +/* Name of the face used to display glyphless characters. */ +Lisp_Object Qglyphless_char; + +/* Char-table to control the display of glyphless characters. */ +Lisp_Object Vglyphless_char_display; + +/* Symbol for the purpose of Vglyphless_char_display. */ +Lisp_Object Qglyphless_char_display; + +/* Method symbols for Vglyphless_char_display. */ +static Lisp_Object Qhexa_code, Qempty_box, Qthin_space, Qzero_width; + +/* Default pixel width of `thin-space' display method. */ +#define THIN_SPACE_WIDTH 1 + /* Default number of seconds to wait before displaying an hourglass cursor. */ #define DEFAULT_HOURGLASS_DELAY 1 @@ -5732,6 +5747,57 @@ (IT)->string))) +/* Lookup the char-table Vglyphless_char_display for character C (-1 + if we want information for no-font case), and return the display + method symbol. By side-effect, update it->what and + it->glyphless_method. This function is called from + get_next_display_element for each character element, and from + x_produce_glyphs when no suitable font was found. */ + +static Lisp_Object +lookup_glyphless_char_display (int c, struct it *it) +{ + Lisp_Object glyphless_method = Qnil; + + if (CHAR_TABLE_P (Vglyphless_char_display) + && CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display)) >= 1) + glyphless_method = (c >= 0 + ? CHAR_TABLE_REF (Vglyphless_char_display, c) + : XCHAR_TABLE (Vglyphless_char_display)->extras[0]); + retry: + if (NILP (glyphless_method)) + { + if (c >= 0) + /* The default is to display the character by a proper font. */ + return Qnil; + /* The default for the no-font case is to display an empty box. */ + glyphless_method = Qempty_box; + } + if (EQ (glyphless_method, Qzero_width)) + { + if (c >= 0) + return glyphless_method; + /* This method can't be used for the no-font case. */ + glyphless_method = Qempty_box; + } + it->what = IT_GLYPHLESS; + if (EQ (glyphless_method, Qthin_space)) + it->glyphless_method = GLYPHLESS_DISPLAY_THIN_SPACE; + else if (EQ (glyphless_method, Qempty_box)) + it->glyphless_method = GLYPHLESS_DISPLAY_EMPTY_BOX; + else if (EQ (glyphless_method, Qhexa_code)) + it->glyphless_method = GLYPHLESS_DISPLAY_HEXA_CODE; + else if (STRINGP (glyphless_method)) + it->glyphless_method = GLYPHLESS_DISPLAY_ACRONYM; + else + { + /* Invalid value. We use the default method. */ + glyphless_method = Qnil; + goto retry; + } + return glyphless_method; +} + /* Load IT's display element fields with information about the next display element from the current position of IT. Value is zero if end of buffer (or C string) is reached. */ @@ -5740,6 +5806,10 @@ static unsigned last_escape_glyph_face_id = (1 << FACE_ID_BITS); static int last_escape_glyph_merged_face_id = 0; +static struct frame *last_glyphless_glyph_frame = NULL; +static unsigned last_glyphless_glyph_face_id = (1 << FACE_ID_BITS); +static int last_glyphless_glyph_merged_face_id = 0; + int get_next_display_element (struct it *it) { @@ -5818,6 +5888,15 @@ goto get_next; } + if (! NILP (lookup_glyphless_char_display (c, it))) + { + if (it->what == IT_GLYPHLESS) + goto done; + /* Don't display this character. */ + set_iterator_to_next (it, 0); + goto get_next; + } + if (! ASCII_CHAR_P (c) && ! NILP (Vnobreak_char_display)) nbsp_or_shy = (c == 0xA0 ? char_is_nbsp : c == 0xAD ? char_is_soft_hyphen @@ -6032,6 +6111,7 @@ } #endif + done: /* Is this character the last one of a run of characters with box? If yes, set IT->end_of_box_run_p to 1. */ if (it->face_box_p @@ -11579,6 +11659,8 @@ reconsider_clip_changes (w, current_buffer); last_escape_glyph_frame = NULL; last_escape_glyph_face_id = (1 << FACE_ID_BITS); + last_glyphless_glyph_frame = NULL; + last_glyphless_glyph_face_id = (1 << FACE_ID_BITS); /* If new fonts have been loaded that make a glyph matrix adjustment necessary, do it. */ @@ -20657,6 +20739,42 @@ } +/* Fill glyph string S from a sequence glyphs for glyphless characters. + See the comment of fill_glyph_string for arguments. + Value is the index of the first glyph not in S. */ + + +static int +fill_glyphless_glyph_string (struct glyph_string *s, int face_id, + int start, int end, int overlaps) +{ + struct glyph *glyph, *last; + int voffset; + + xassert (s->first_glyph->type == GLYPHLESS_GLYPH); + s->for_overlaps = overlaps; + glyph = s->row->glyphs[s->area] + start; + last = s->row->glyphs[s->area] + end; + voffset = glyph->voffset; + s->face = FACE_FROM_ID (s->f, face_id); + s->font = s->face->font; + s->nchars = 1; + s->width = glyph->pixel_width; + glyph++; + while (glyph < last + && glyph->type == GLYPHLESS_GLYPH + && glyph->voffset == voffset + && glyph->face_id == face_id) + { + s->nchars++; + s->width += glyph->pixel_width; + glyph++; + } + s->ybase += voffset; + return glyph - s->row->glyphs[s->area]; +} + + /* Fill glyph string S from a sequence of character glyphs. FACE_ID is the face id of the string. START is the index of the @@ -21167,6 +21285,28 @@ } while (0) +/* Add a glyph string for a sequence of glyphless character's glyphs + to the list of strings between HEAD and TAIL. The meanings of + arguments are the same as those of BUILD_CHAR_GLYPH_STRINGS. */ + +#define BUILD_GLYPHLESS_GLYPH_STRING(START, END, HEAD, TAIL, HL, X, LAST_X) \ + do \ + { \ + int face_id; \ + XChar2b *char2b; \ + \ + face_id = (row)->glyphs[area][START].face_id; \ + \ + s = (struct glyph_string *) alloca (sizeof *s); \ + INIT_GLYPH_STRING (s, NULL, w, row, area, START, HL); \ + append_glyph_string (&HEAD, &TAIL, s); \ + s->x = (X); \ + START = fill_glyphless_glyph_string (s, face_id, START, END, \ + overlaps); \ + } \ + while (0) + + /* Build a list of glyph strings between HEAD and TAIL for the glyphs of AREA of glyph row ROW on window W between indices START and END. HL overrides the face for drawing glyph strings, e.g. it is @@ -21190,7 +21330,7 @@ BUILD_CHAR_GLYPH_STRINGS (START, END, HEAD, TAIL, \ HL, X, LAST_X); \ break; \ - \ + \ case COMPOSITE_GLYPH: \ if (first_glyph->u.cmp.automatic) \ BUILD_GSTRING_GLYPH_STRING (START, END, HEAD, TAIL, \ @@ -21199,21 +21339,26 @@ BUILD_COMPOSITE_GLYPH_STRING (START, END, HEAD, TAIL, \ HL, X, LAST_X); \ break; \ - \ + \ case STRETCH_GLYPH: \ BUILD_STRETCH_GLYPH_STRING (START, END, HEAD, TAIL, \ HL, X, LAST_X); \ break; \ - \ + \ case IMAGE_GLYPH: \ BUILD_IMAGE_GLYPH_STRING (START, END, HEAD, TAIL, \ HL, X, LAST_X); \ break; \ - \ + \ + case GLYPHLESS_GLYPH: \ + BUILD_GLYPHLESS_GLYPH_STRING (START, END, HEAD, TAIL, \ + HL, X, LAST_X); \ + break; \ + \ default: \ abort (); \ } \ - \ + \ if (s) \ { \ set_glyph_string_background_width (s, START, LAST_X); \ @@ -22109,6 +22254,229 @@ } +/* Append a glyph for a glyphless character to IT->glyph_row. FACE_ID + is a face ID to be used for the glyph. FOR_NO_FONT is nonzero if + and only if this is for a character for which no font was found. + + If the display method (it->glyphless_method) is + GLYPHLESS_DISPLAY_ACRONYM or GLYPHLESS_DISPLAY_HEXA_CODE, LEN is a + length of the acronym or the hexadecimal string, UPPER_XOFF and + UPPER_YOFF are pixel offsets for the upper part of the string, + LOWER_XOFF and LOWER_YOFF are for the lower part. + + For the other display methods, LEN through LOWER_YOFF are zero. */ + +static void +append_glyphless_glyph (struct it *it, int face_id, int for_no_font, int len, + short upper_xoff, short upper_yoff, + short lower_xoff, short lower_yoff) +{ + struct glyph *glyph; + enum glyph_row_area area = it->area; + + glyph = it->glyph_row->glyphs[area] + it->glyph_row->used[area]; + if (glyph < it->glyph_row->glyphs[area + 1]) + { + /* If the glyph row is reversed, we need to prepend the glyph + rather than append it. */ + if (it->glyph_row->reversed_p && area == TEXT_AREA) + { + struct glyph *g; + + /* Make room for the additional glyph. */ + for (g = glyph - 1; g >= it->glyph_row->glyphs[area]; g--) + g[1] = *g; + glyph = it->glyph_row->glyphs[area]; + } + glyph->charpos = CHARPOS (it->position); + glyph->object = it->object; + glyph->pixel_width = it->pixel_width; + glyph->ascent = it->ascent; + glyph->descent = it->descent; + glyph->voffset = it->voffset; + glyph->type = GLYPHLESS_GLYPH; + glyph->u.glyphless.method = it->glyphless_method; + glyph->u.glyphless.for_no_font = for_no_font; + glyph->u.glyphless.len = len; + glyph->u.glyphless.ch = it->c; + glyph->slice.glyphless.upper_xoff = upper_xoff; + glyph->slice.glyphless.upper_yoff = upper_yoff; + glyph->slice.glyphless.lower_xoff = lower_xoff; + glyph->slice.glyphless.lower_yoff = lower_yoff; + glyph->avoid_cursor_p = it->avoid_cursor_p; + glyph->multibyte_p = it->multibyte_p; + glyph->left_box_line_p = it->start_of_box_run_p; + glyph->right_box_line_p = it->end_of_box_run_p; + glyph->overlaps_vertically_p = (it->phys_ascent > it->ascent + || it->phys_descent > it->descent); + glyph->padding_p = 0; + glyph->glyph_not_available_p = 0; + glyph->face_id = face_id; + glyph->font_type = FONT_TYPE_UNKNOWN; + if (it->bidi_p) + { + glyph->resolved_level = it->bidi_it.resolved_level; + if ((it->bidi_it.type & 7) != it->bidi_it.type) + abort (); + glyph->bidi_type = it->bidi_it.type; + } + ++it->glyph_row->used[area]; + } + else + IT_EXPAND_MATRIX_WIDTH (it, area); +} + + +/* Produce a glyph for a glyphless character for iterator IT. + IT->glyphless_method specifies which method to use for displaying + the glyph. See the description of enum glyphless_display_method in + dispextern.h for the default of the display methods. + + FOR_NO_FONT is nonzero if and only if this is for a character for + which no font was found. ACRONYM, if non-nil, is an acronym string + for the character. */ + +static void +produce_glyphless_glyph (struct it *it, int for_no_font, Lisp_Object acronym) +{ + int face_id; + struct face *face; + struct font *font; + int base_width, base_height, width, height; + short upper_xoff, upper_yoff, lower_xoff, lower_yoff; + int len; + + /* Get the metrics of the base font. We always refer to the current + ASCII face. */ + face = FACE_FROM_ID (it->f, it->face_id)->ascii_face; + font = face->font ? face->font : FRAME_FONT (it->f); + it->ascent = FONT_BASE (font) + font->baseline_offset; + it->descent = FONT_DESCENT (font) - font->baseline_offset; + base_height = it->ascent + it->descent; + base_width = font->average_width; + + /* Get a face ID for the glyph by utilizing a cache (the same way as + doen for `escape-glyph' in get_next_display_element). */ + if (it->f == last_glyphless_glyph_frame + && it->face_id == last_glyphless_glyph_face_id) + { + face_id = last_glyphless_glyph_merged_face_id; + } + else + { + /* Merge the `glyphless-char' face into the current face. */ + face_id = merge_faces (it->f, Qglyphless_char, 0, it->face_id); + last_glyphless_glyph_frame = it->f; + last_glyphless_glyph_face_id = it->face_id; + last_glyphless_glyph_merged_face_id = face_id; + } + + if (it->glyphless_method == GLYPHLESS_DISPLAY_THIN_SPACE) + { + it->pixel_width = THIN_SPACE_WIDTH; + len = 0; + upper_xoff = upper_yoff = lower_xoff = lower_yoff = 0; + } + else if (it->glyphless_method == GLYPHLESS_DISPLAY_EMPTY_BOX) + { + width = CHAR_WIDTH (it->c); + if (width == 0) + width = 1; + else if (width > 4) + width = 4; + it->pixel_width = base_width * width; + len = 0; + upper_xoff = upper_yoff = lower_xoff = lower_yoff = 0; + } + else + { + char buf[7], *str; + unsigned int code[6]; + int upper_len; + int ascent, descent; + struct font_metrics metrics_upper, metrics_lower; + + face = FACE_FROM_ID (it->f, face_id); + font = face->font ? face->font : FRAME_FONT (it->f); + PREPARE_FACE_FOR_DISPLAY (it->f, face); + + if (it->glyphless_method == GLYPHLESS_DISPLAY_ACRONYM) + { + if (! STRINGP (acronym) && CHAR_TABLE_P (Vglyphless_char_display)) + acronym = CHAR_TABLE_REF (Vglyphless_char_display, it->c); + str = STRINGP (acronym) ? (char *) SDATA (acronym) : ""; + } + else + { + xassert (it->glyphless_method == GLYPHLESS_DISPLAY_HEXA_CODE); + sprintf (buf, "%0*X", it->c < 0x10000 ? 4 : 6, it->c); + str = buf; + } + for (len = 0; str[len] && ASCII_BYTE_P (str[len]); len++) + code[len] = font->driver->encode_char (font, str[len]); + upper_len = (len + 1) / 2; + font->driver->text_extents (font, code, upper_len, + &metrics_upper); + font->driver->text_extents (font, code + upper_len, len - upper_len, + &metrics_lower); + + + + /* +4 is for vertical bars of a box plus 1-pixel spaces at both side. */ + width = max (metrics_upper.width, metrics_lower.width) + 4; + upper_xoff = upper_yoff = 2; /* the typical case */ + if (base_width >= width) + { + /* Align the upper to the left, the lower to the right. */ + it->pixel_width = base_width; + lower_xoff = base_width - 2 - metrics_lower.width; + } + else + { + /* Center the shorter one. */ + it->pixel_width = width; + if (metrics_upper.width >= metrics_lower.width) + lower_xoff = (width - metrics_lower.width) / 2; + else + upper_xoff = (width - metrics_upper.width) / 2; + } + + /* +5 is for horizontal bars of a box plus 1-pixel spaces at + top, bottom, and between upper and lower strings. */ + height = (metrics_upper.ascent + metrics_upper.descent + + metrics_lower.ascent + metrics_lower.descent) + 5; + /* Center vertically. + H:base_height, D:base_descent + h:height, ld:lower_descent, la:lower_ascent, ud:upper_descent + + ascent = - (D - H/2 - h/2 + 1); "+ 1" for rounding up + descent = D - H/2 + h/2; + lower_yoff = descent - 2 - ld; + upper_yoff = lower_yoff - la - 1 - ud; */ + ascent = - (it->descent - (base_height + height + 1) / 2); + descent = it->descent - (base_height - height) / 2; + lower_yoff = descent - 2 - metrics_lower.descent; + upper_yoff = (lower_yoff - metrics_lower.ascent - 1 + - metrics_upper.descent); + /* Don't make the height shorter than the base height. */ + if (height > base_height) + { + it->ascent = ascent; + it->descent = descent; + } + } + + it->phys_ascent = it->ascent; + it->phys_descent = it->descent; + if (it->glyph_row) + append_glyphless_glyph (it, face_id, for_no_font, len, + upper_xoff, upper_yoff, + lower_xoff, lower_yoff); + it->nglyphs = 1; + take_vertical_position_into_account (it); +} + + /* RIF: Produce glyphs/get display metrics for the display element IT is loaded with. See the description of struct it in dispextern.h @@ -22126,28 +22494,24 @@ XChar2b char2b; struct face *face = FACE_FROM_ID (it->f, it->face_id); struct font *font = face->font; - int font_not_found_p = font == NULL; struct font_metrics *pcm = NULL; int boff; /* baseline offset */ - if (font_not_found_p) - { - /* When no suitable font found, display an empty box based - on the metrics of the font of the default face (or what - remapped). */ - struct face *no_font_face - = FACE_FROM_ID (it->f, - NILP (Vface_remapping_alist) ? DEFAULT_FACE_ID - : lookup_basic_face (it->f, DEFAULT_FACE_ID)); - font = no_font_face->font; - boff = font->baseline_offset; - } - else - { - boff = font->baseline_offset; - if (font->vertical_centering) - boff = VCENTER_BASELINE_OFFSET (font, it->f) - boff; - } + if (font == NULL) + { + /* When no suitable font is found, display this character by + the method specified in the first extra slot of + Vglyphless_char_display. */ + Lisp_Object acronym = lookup_glyphless_char_display (-1, it); + + xassert (it->what == IT_GLYPHLESS); + produce_glyphless_glyph (it, 1, STRINGP (acronym) ? acronym : Qnil); + goto done; + } + + boff = font->baseline_offset; + if (font->vertical_centering) + boff = VCENTER_BASELINE_OFFSET (font, it->f) - boff; if (it->char_to_display != '\n' && it->char_to_display != '\t') { @@ -22167,8 +22531,7 @@ it->descent = FONT_DESCENT (font) - boff; } - if (! font_not_found_p - && get_char_glyph_code (it->char_to_display, font, &char2b)) + if (get_char_glyph_code (it->char_to_display, font, &char2b)) { pcm = get_per_char_metric (it->f, font, &char2b); if (pcm->width == 0 @@ -22758,11 +23121,14 @@ if (it->glyph_row) append_composite_glyph (it); } + else if (it->what == IT_GLYPHLESS) + produce_glyphless_glyph (it, 0, Qnil); else if (it->what == IT_IMAGE) produce_image_glyph (it); else if (it->what == IT_STRETCH) produce_stretch_glyph (it); + done: /* Accumulate dimensions. Note: can't assume that it->descent > 0 because this isn't true for images with `:ascent 100'. */ xassert (it->ascent >= 0 && it->descent >= 0); @@ -26592,6 +26958,35 @@ hourglass_atimer = NULL; hourglass_shown_p = 0; + + DEFSYM (Qglyphless_char, "glyphless-char"); + DEFSYM (Qhexa_code, "hexa-code"); + DEFSYM (Qempty_box, "empty-box"); + DEFSYM (Qthin_space, "thin-space"); + DEFSYM (Qzero_width, "zero-width"); + + DEFSYM (Qglyphless_char_display, "glyphless-char-display"); + /* Intern this now in case it isn't already done. + Setting this variable twice is harmless. + But don't staticpro it here--that is done in alloc.c. */ + Qchar_table_extra_slots = intern_c_string ("char-table-extra-slots"); + Fput (Qglyphless_char_display, Qchar_table_extra_slots, make_number (1)); + + DEFVAR_LISP ("glyphless-char-display", &Vglyphless_char_display, + doc: /* Char-table to control displaying of glyphless characters. +Each element, if non-nil, is an ASCII acronym string (displayed in a box) +or one of these symbols: + hexa-code: display with hexadecimal character code in a box + empty-box: display with an empty box + thin-space: display with 1-pixel width space + zero-width: don't display + +It has one extra slot to control the display of a character for which +no font is found. The value of the slot is `hexa-code' or `empty-box'. +The default is `empty-box'. */); + Vglyphless_char_display = Fmake_char_table (Qglyphless_char_display, Qnil); + Fset_char_table_extra_slot (Vglyphless_char_display, make_number (0), + Qempty_box); } diff -r 0576327092ab -r 24c75a5f002a src/xterm.c --- a/src/xterm.c Thu Oct 28 19:57:59 2010 +0900 +++ b/src/xterm.c Fri Oct 29 09:50:13 2010 +0900 @@ -1330,6 +1330,83 @@ } +/* Draw the foreground of glyph string S for glyphless characters. */ + +static void +x_draw_glyphless_glyph_string_foreground (struct glyph_string *s) +{ + struct glyph *glyph = s->first_glyph; + XChar2b char2b[8]; + int x, i, j; + + /* If first glyph of S has a left box line, start drawing the text + of S to the right of that box line. */ + if (s->face && s->face->box != FACE_NO_BOX + && s->first_glyph->left_box_line_p) + x = s->x + eabs (s->face->box_line_width); + else + x = s->x; + + s->char2b = char2b; + + for (i = 0; i < s->nchars; i++, glyph++) + { + char buf[7], *str = NULL; + int len = glyph->u.glyphless.len; + + if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM) + { + if (len > 0 + && CHAR_TABLE_P (Vglyphless_char_display) + && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display)) + >= 1)) + { + Lisp_Object acronym + = (! glyph->u.glyphless.for_no_font + ? CHAR_TABLE_REF (Vglyphless_char_display, + glyph->u.glyphless.ch) + : XCHAR_TABLE (Vglyphless_char_display)->extras[0]); + if (STRINGP (acronym)) + str = (char *) SDATA (acronym); + } + } + else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEXA_CODE) + { + sprintf ((char *) buf, "%0*X", + glyph->u.glyphless.ch < 0x10000 ? 4 : 6, + glyph->u.glyphless.ch); + str = buf; + } + + if (str) + { + int upper_len = (len + 1) / 2; + unsigned code; + + /* It is assured that all LEN characters in STR is ASCII. */ + for (j = 0; j < len; j++) + { + code = s->font->driver->encode_char (s->font, str[j]); + STORE_XCHAR2B (char2b + j, code >> 8, code & 0xFF); + } + s->font->driver->draw (s, 0, upper_len, + x + glyph->slice.glyphless.upper_xoff, + s->ybase + glyph->slice.glyphless.upper_yoff, + 0); + s->font->driver->draw (s, upper_len, len, + x + glyph->slice.glyphless.lower_xoff, + s->ybase + glyph->slice.glyphless.lower_yoff, + 0); + } + if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE) + XDrawRectangle (s->display, s->window, s->gc, + x, s->ybase - glyph->ascent, + glyph->pixel_width - 1, + glyph->ascent + glyph->descent - 1); + x += glyph->pixel_width; + } +} + #ifdef USE_X_TOOLKIT static struct frame *x_frame_of_widget (Widget); @@ -2656,6 +2733,14 @@ x_draw_composite_glyph_string_foreground (s); break; + case GLYPHLESS_GLYPH: + if (s->for_overlaps) + s->background_filled_p = 1; + else + x_draw_glyph_string_background (s, 1); + x_draw_glyphless_glyph_string_foreground (s); + break; + default: abort (); }