(optimize-char-table (standard-category-table))
+\f
+;; 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)
\f
;;; Setting word boundary.
/* 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
(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. */
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)
{
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
}
#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
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. */
}
+/* 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
} 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
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, \
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); \
}
+/* 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
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
+ if (font == NULL)
{
- boff = font->baseline_offset;
- if (font->vertical_centering)
- boff = VCENTER_BASELINE_OFFSET (font, it->f) - boff;
+ /* 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')
{
int stretched_p;
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
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);
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);
}