1 /* Composite sequence support.
2 Copyright (C) 1999 Electrotechnical Laboratory, JAPAN.
3 Licensed to the Free Software Foundation.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
26 #include "intervals.h"
28 /* Emacs uses special text property `composition' to support character
29 composition. A sequence of characters that have the same (i.e. eq)
30 `composition' property value is treated as a single composite
31 sequence (we call it just `composition' here after). Characters in
32 a composition are all composed somehow on the screen.
34 The property value has this form when the composition is made:
35 ((LENGTH . COMPONENTS) . MODIFICATION-FUNC)
36 then turns to this form:
37 (COMPOSITION-ID . (LENGTH COMPONENTS-VEC . MODIFICATION-FUNC))
38 when the composition is registered in composition_hash_table and
39 composition_table. These rather peculiar structures were designed
40 to make it easy to distinguish them quickly (we can do that by
41 checking only the first element) and to extract LENGTH (from the
42 former form) and COMPOSITION-ID (from the latter form).
44 We register a composition when it is displayed, or when the width
45 is required (for instance, to calculate columns).
47 LENGTH -- Length of the composition. This information is used to
48 check the validity of the composition.
50 COMPONENTS -- Character, string, vector, list, or nil.
52 If it is nil, characters in the text are composed relatively
53 according to their metrics in font glyphs.
55 If it is a character or a string, the character or characters
56 in the string are composed relatively.
58 If it is a vector or list of integers, the element is a
59 character or an encoded composition rule. The characters are
60 composed according to the rules. (2N)th elements are
61 characters to be composed and (2N+1)th elements are
62 composition rules to tell how to compose (2N+2)th element with
63 the previously composed 2N glyphs.
65 COMPONENTS-VEC -- Vector of integers. In relative composition, the
66 elements are characters to be composed. In rule-base
67 composition, the elements are characters or encoded
70 MODIFICATION-FUNC -- If non nil, it is a function to call when the
71 composition gets invalid after a modification in a buffer. If
72 it is nil, a function in `composition-function-table' of the
73 first character in the sequence is called.
75 COMPOSITION-ID --Identification number of the composition. It is
76 used as an index to composition_table for the composition.
78 When Emacs has to display a composition or has to know its
79 displaying width, the function get_composition_id is called. It
80 returns COMPOSITION-ID so that the caller can access the
81 information about the composition through composition_table. If a
82 COMPOSITION-ID has not yet been assigned to the composition,
83 get_composition_id checks the validity of `composition' property,
84 and, if valid, assigns a new ID, registers the information in
85 composition_hash_table and composition_table, and changes the form
86 of the property value. If the property is invalid, return -1
87 without changing the property value.
89 We use two tables to keep information about composition;
90 composition_hash_table and composition_table.
92 The former is a hash table in which keys are COMPONENTS-VECs and
93 values are the corresponding COMPOSITION-IDs. This hash table is
94 weak, but as each key (COMPONENTS-VEC) is also kept as a value of
95 `composition' property, it won't be collected as garbage until all
96 text that have the same COMPONENTS-VEC are deleted.
98 The latter is a table of pointers to `struct composition' indexed
99 by COMPOSITION-ID. This structure keep the other information (see
102 In general, a text property holds information about individual
103 characters. But, a `composition' property holds information about
104 a sequence of characters (in this sense, it is like `intangible'
105 property). That means that we should not share the property value
106 in adjacent compositions we can't distinguish them if they have the
107 same property. So, after any changes, we call
108 `update_compositions' and change a property of one of adjacent
109 compositions to a copy of it. This function also runs a proper
110 composition modification function to make a composition that gets
111 invalid by the change valid again.
113 As a value of `composition' property holds information about a
114 specific range of text, the value gets invalid if we change the
115 text in the range. We treat `composition' property always
116 rear-nonsticky (currently by setting default-text-properties to
117 (rear-nonsticky (composition))) and we never make properties of
118 adjacent compositions identical. Thus, any such changes make the
119 range just shorter. So, we can check the validity of `composition'
120 property by comparing LENGTH information with the actual length of
126 Lisp_Object Qcomposition
;
128 /* Table of pointers to the structure `composition' indexed by
129 COMPOSITION-ID. This structure is for storing information about
130 each composition except for COMPONENTS-VEC. */
131 struct composition
**composition_table
;
133 /* The current size of `composition_table'. */
134 static int composition_table_size
;
136 /* Number of compositions currently made. */
139 /* Hash table for compositions. The key is COMPONENTS-VEC of
140 `composition' property. The value is the corresponding
142 Lisp_Object composition_hash_table
;
144 /* Function to call to adjust composition. */
145 Lisp_Object Vcompose_chars_after_function
;
147 /* Temporary variable used in macros COMPOSITION_XXX. */
148 Lisp_Object composition_temp
;
150 /* Return how many columns C will occupy on the screen. It always
151 returns 1 for control characters and 8-bit characters because those
152 are just ignored in a composition. */
153 #define CHAR_WIDTH(c) \
154 (SINGLE_BYTE_CHAR_P (c) ? 1 : CHARSET_WIDTH (CHAR_CHARSET (c)))
156 /* The following macros for hash table are copied from fns.c. */
157 /* Return the contents of vector V at index IDX. */
158 #define AREF(V, IDX) XVECTOR (V)->contents[IDX]
159 /* Value is the key part of entry IDX in hash table H. */
160 #define HASH_KEY(H, IDX) AREF ((H)->key_and_value, 2 * (IDX))
161 /* Value is the value part of entry IDX in hash table H. */
162 #define HASH_VALUE(H, IDX) AREF ((H)->key_and_value, 2 * (IDX) + 1)
164 /* Return COMPOSITION-ID of a composition at buffer position
165 CHARPOS/BYTEPOS and length NCHARS. The `composition' property of
166 the sequence is PROP. STRING, if non-nil, is a string that
167 contains the composition instead of the current buffer.
169 If the composition is invalid, return -1. */
172 get_composition_id (charpos
, bytepos
, nchars
, prop
, string
)
173 int charpos
, bytepos
, nchars
;
174 Lisp_Object prop
, string
;
176 Lisp_Object id
, length
, components
, key
, *key_contents
;
178 struct Lisp_Hash_Table
*hash_table
= XHASH_TABLE (composition_hash_table
);
181 struct composition
*cmp
;
185 Form-A: ((LENGTH . COMPONENTS) . MODIFICATION-FUNC)
187 Form-B: (COMPOSITION-ID . (LENGTH COMPONENTS-VEC . MODIFICATION-FUNC))
189 if (nchars
== 0 || !CONSP (prop
))
190 goto invalid_composition
;
195 /* PROP should be Form-B. */
196 if (XINT (id
) < 0 || XINT (id
) >= n_compositions
)
197 goto invalid_composition
;
201 /* PROP should be Form-A.
202 Thus, ID should be (LENGTH . COMPONENTS). */
204 goto invalid_composition
;
206 if (!INTEGERP (length
) || XINT (length
) != nchars
)
207 goto invalid_composition
;
209 components
= XCDR (id
);
211 /* Check if the same composition has already been registered or not
212 by consulting composition_hash_table. The key for this table is
213 COMPONENTS (converted to a vector COMPONENTS-VEC) or, if it is
214 nil, vector of characters in the composition range. */
215 if (INTEGERP (components
))
216 key
= Fmake_vector (make_number (1), components
);
217 else if (STRINGP (components
) || CONSP (components
))
218 key
= Fvconcat (1, &components
);
219 else if (VECTORP (components
))
221 else if (NILP (components
))
223 key
= Fmake_vector (make_number (nchars
), Qnil
);
224 if (STRINGP (string
))
225 for (i
= 0; i
< nchars
; i
++)
227 FETCH_STRING_CHAR_ADVANCE (ch
, string
, charpos
, bytepos
);
228 XVECTOR (key
)->contents
[i
] = make_number (ch
);
231 for (i
= 0; i
< nchars
; i
++)
233 FETCH_CHAR_ADVANCE (ch
, charpos
, bytepos
);
234 XVECTOR (key
)->contents
[i
] = make_number (ch
);
238 goto invalid_composition
;
240 hash_index
= hash_lookup (hash_table
, key
, &hash_code
);
243 /* We have already registered the same composition. Change PROP
244 from Form-A above to Form-B while replacing COMPONENTS with
245 COMPONENTS-VEC stored in the hash table. We can directly
246 modify the cons cell of PROP because it is not shared. */
247 key
= HASH_KEY (hash_table
, hash_index
);
248 id
= HASH_VALUE (hash_table
, hash_index
);
250 XCDR (prop
) = Fcons (make_number (nchars
), Fcons (key
, XCDR (prop
)));
254 /* This composition is a new one. We must register it. */
256 /* Check if we have sufficient memory to store this information. */
257 if (composition_table_size
== 0)
259 composition_table_size
= 256;
261 = (struct composition
**) xmalloc (sizeof (composition_table
[0])
262 * composition_table_size
);
264 else if (composition_table_size
<= n_compositions
)
266 composition_table_size
+= 256;
268 = (struct composition
**) xrealloc (composition_table
,
269 sizeof (composition_table
[0])
270 * composition_table_size
);
273 key_contents
= XVECTOR (key
)->contents
;
275 /* Check if the contents of COMPONENTS are valid if COMPONENTS is a
276 vector or a list. It should be a sequence of:
277 char1 rule1 char2 rule2 char3 ... ruleN charN+1 */
278 if (VECTORP (components
) || CONSP (components
))
280 int len
= XVECTOR (key
)->size
;
282 /* The number of elements should be odd. */
284 goto invalid_composition
;
285 /* All elements should be integers (character or encoded
286 composition rule). */
287 for (i
= 0; i
< len
; i
++)
289 if (!INTEGERP (key_contents
[i
]))
290 goto invalid_composition
;
294 /* Change PROP from Form-A above to Form-B. We can directly modify
295 the cons cell of PROP because it is not shared. */
296 XSETFASTINT (id
, n_compositions
);
298 XCDR (prop
) = Fcons (make_number (nchars
), Fcons (key
, XCDR (prop
)));
300 /* Register the composition in composition_hash_table. */
301 hash_index
= hash_put (hash_table
, key
, id
, hash_code
);
303 /* Register the composition in composition_table. */
304 cmp
= (struct composition
*) xmalloc (sizeof (struct composition
));
306 cmp
->method
= (NILP (components
)
307 ? COMPOSITION_RELATIVE
308 : ((INTEGERP (components
) || STRINGP (components
))
309 ? COMPOSITION_WITH_ALTCHARS
310 : COMPOSITION_WITH_RULE_ALTCHARS
));
311 cmp
->hash_index
= hash_index
;
312 glyph_len
= (cmp
->method
== COMPOSITION_WITH_RULE_ALTCHARS
313 ? (XVECTOR (key
)->size
+ 1) / 2
314 : XVECTOR (key
)->size
);
315 cmp
->glyph_len
= glyph_len
;
316 cmp
->offsets
= (short *) xmalloc (sizeof (short) * glyph_len
* 2);
319 /* Calculate the width of overall glyphs of the composition. */
320 if (cmp
->method
!= COMPOSITION_WITH_RULE_ALTCHARS
)
322 /* Relative composition. */
324 for (i
= 0; i
< glyph_len
; i
++)
327 ch
= XINT (key_contents
[i
]);
328 this_width
= CHAR_WIDTH (ch
);
329 if (cmp
->width
< this_width
)
330 cmp
->width
= this_width
;
335 /* Rule-base composition. */
336 float leftmost
= 0.0, rightmost
;
338 ch
= XINT (key_contents
[0]);
339 rightmost
= CHAR_WIDTH (ch
);
341 for (i
= 1; i
< glyph_len
; i
+= 2)
343 int rule
, gref
, nref
;
347 rule
= XINT (key_contents
[i
]);
348 ch
= XINT (key_contents
[i
+ 1]);
349 this_width
= CHAR_WIDTH (ch
);
351 /* A composition rule is specified by an integer value
352 that encodes global and new reference points (GREF and
353 NREF). GREF and NREF are specified by numbers as
361 ---3---4---5--- baseline
365 COMPOSITION_DECODE_RULE (rule
, gref
, nref
);
366 this_left
= (leftmost
367 + (gref
% 3) * (rightmost
- leftmost
) / 2.0
368 - (nref
% 3) * this_width
/ 2.0);
370 if (this_left
< leftmost
)
371 leftmost
= this_left
;
372 if (this_left
+ this_width
> rightmost
)
373 rightmost
= this_left
+ this_width
;
376 cmp
->width
= rightmost
- leftmost
;
377 if (cmp
->width
< (rightmost
- leftmost
))
378 /* To get a ceiling integer value. */
382 composition_table
[n_compositions
] = cmp
;
384 return n_compositions
++;
387 /* Would it be better to remove this `composition' property? */
392 /* Find a composition at or nearest to position POS of OBJECT (buffer
395 OBJECT defaults to the current buffer. If there's a composition at
396 POS, set *START and *END to the start and end of the sequence,
397 *PROP to the `composition' property, and return 1.
399 If there's no composition at POS and LIMIT is negative, return 0.
401 Otherwise, search for a composition forward (LIMIT > POS) or
402 backward (LIMIT < POS). In this case, LIMIT bounds the search.
404 If a composition is found, set *START, *END, and *PROP as above,
405 and return 1, else return 0.
407 This doesn't check the validity of composition. */
410 find_composition (pos
, limit
, start
, end
, prop
, object
)
411 int pos
, limit
, *start
, *end
;
412 Lisp_Object
*prop
, object
;
416 if (get_property_and_range (pos
, Qcomposition
, prop
, start
, end
, object
))
419 if (limit
< 0 || limit
== pos
)
422 if (limit
> pos
) /* search forward */
423 val
= Fnext_single_property_change (make_number (pos
), Qcomposition
,
424 object
, make_number (limit
));
425 else /* search backward */
426 val
= Fprevious_single_property_change (make_number (pos
), Qcomposition
,
427 object
, make_number (limit
));
431 get_property_and_range (pos
, Qcomposition
, prop
, start
, end
, object
);
435 /* Run a proper function to adjust the composition sitting between
436 FROM and TO with property PROP. */
439 run_composition_function (from
, to
, prop
)
443 Lisp_Object func
, val
;
446 func
= COMPOSITION_MODIFICATION_FUNC (prop
);
447 /* If an invalid composition precedes or follows, try to make them
450 && find_composition (from
- 1, -1, &start
, &end
, &prop
, Qnil
)
451 && !COMPOSITION_VALID_P (start
, end
, prop
))
454 && find_composition (to
, -1, &start
, &end
, &prop
, Qnil
)
455 && !COMPOSITION_VALID_P (start
, end
, prop
))
458 call2 (func
, make_number (from
), make_number (to
));
459 else if (!NILP (Ffboundp (Vcompose_chars_after_function
)))
460 call2 (Vcompose_chars_after_function
,
461 make_number (from
), make_number (to
));
464 /* Make invalid compositions adjacent to or inside FROM and TO valid.
465 CHECK_MASK is bitwise `or' of mask bits defined by macros
466 CHECK_XXX (see the comment in composite.h).
468 This function is called when a buffer text is changed. If the
469 change is deletion, FROM == TO. Otherwise, FROM < TO. */
472 update_compositions (from
, to
, check_mask
)
475 Lisp_Object prop
, hook
;
478 /* If FROM and TO are not in a valid range, do nothing. */
479 if (! (BEGV
<= from
&& from
<= to
&& to
<= ZV
))
482 if (check_mask
& CHECK_HEAD
)
484 /* FROM should be at composition boundary. But, insertion or
485 deletion will make two compositions adjacent and
486 indistinguishable when they have same (eq) property. To
487 avoid it, in such a case, we change the property of the
488 latter to the copy of it. */
490 && find_composition (from
- 1, -1, &start
, &end
, &prop
, Qnil
))
493 Fput_text_property (make_number (from
), make_number (end
),
495 Fcons (XCAR (prop
), XCDR (prop
)), Qnil
);
496 run_composition_function (start
, end
, prop
);
500 && find_composition (from
, -1, &start
, &from
, &prop
, Qnil
))
501 run_composition_function (start
, from
, prop
);
504 if (check_mask
& CHECK_INSIDE
)
506 /* In this case, we are sure that (check & CHECK_TAIL) is also
507 nonzero. Thus, here we should check only compositions before
510 && find_composition (from
, to
, &start
, &from
, &prop
, Qnil
)
512 run_composition_function (start
, from
, prop
);
515 if (check_mask
& CHECK_TAIL
)
518 && find_composition (to
- 1, -1, &start
, &end
, &prop
, Qnil
))
520 /* TO should be also at composition boundary. But,
521 insertion or deletion will make two compositions adjacent
522 and indistinguishable when they have same (eq) property.
523 To avoid it, in such a case, we change the property of
524 the former to the copy of it. */
526 Fput_text_property (make_number (start
), make_number (to
),
528 Fcons (XCAR (prop
), XCDR (prop
)), Qnil
);
529 run_composition_function (start
, end
, prop
);
532 && find_composition (to
, -1, &start
, &end
, &prop
, Qnil
))
533 run_composition_function (start
, end
, prop
);
538 /* Modify composition property values in LIST destructively. LIST is
539 a list as returned from text_property_list. Change values to the
540 top-level copies of them so that none of them are `eq'. */
543 make_composition_value_copy (list
)
546 Lisp_Object plist
, val
;
548 for (; CONSP (list
); list
= XCDR (list
))
550 plist
= XCAR (XCDR (XCDR (XCAR (list
))));
551 while (CONSP (plist
) && CONSP (XCDR (plist
)))
553 if (EQ (XCAR (plist
), Qcomposition
)
554 && (val
= XCAR (XCDR (plist
)), CONSP (val
)))
555 XCAR (XCDR (plist
)) = Fcons (XCAR (val
), XCDR (val
));
556 plist
= XCDR (XCDR (plist
));
562 /* Make text in the region between START and END a composition that
563 has COMPONENTS and MODIFICATION-FUNC.
565 If STRING is non-nil, then operate on characters contained between
566 indices START and END in STRING. */
569 compose_text (start
, end
, components
, modification_func
, string
)
571 Lisp_Object components
, modification_func
, string
;
575 prop
= Fcons (Fcons (make_number (end
- start
), components
),
577 Fput_text_property (make_number (start
), make_number (end
),
578 Qcomposition
, prop
, string
);
582 /* Emacs Lisp APIs. */
584 DEFUN ("compose-region-internal", Fcompose_region_internal
,
585 Scompose_region_internal
, 2, 4, 0,
586 "Internal use only.\n\
588 Compose text in the region between START and END.\n\
589 Optional 3rd and 4th arguments are COMPONENTS and MODIFICATION-FUNC\n\
590 for the composition. See `compose-region' for more detial.")
591 (start
, end
, components
, mod_func
)
592 Lisp_Object start
, end
, components
, mod_func
;
594 validate_region (&start
, &end
);
595 if (!NILP (components
)
596 && !INTEGERP (components
)
597 && !CONSP (components
)
598 && !STRINGP (components
))
599 CHECK_VECTOR (components
, 2);
601 compose_text (XINT (start
), XINT (end
), components
, mod_func
, Qnil
);
605 DEFUN ("compose-string-internal", Fcompose_string_internal
,
606 Scompose_string_internal
, 3, 5, 0,
607 "Internal use only.\n\
609 Compose text between indices START and END of STRING.\n\
610 Optional 4th and 5th arguments are COMPONENTS and MODIFICATION-FUNC\n\
611 for the composition. See `compose-string' for more detial.")
612 (string
, start
, end
, components
, mod_func
)
613 Lisp_Object string
, start
, end
, components
, mod_func
;
615 CHECK_STRING (string
, 0);
616 CHECK_NUMBER (start
, 1);
617 CHECK_NUMBER (end
, 2);
619 if (XINT (start
) < 0 ||
620 XINT (start
) > XINT (end
)
621 || XINT (end
) > XSTRING (string
)->size
)
622 args_out_of_range (start
, end
);
624 compose_text (XINT (start
), XINT (end
), components
, mod_func
, string
);
628 DEFUN ("find-composition-internal", Ffind_composition_internal
,
629 Sfind_composition_internal
, 4, 4, 0,
630 "Internal use only.\n\
632 Return information about composition at or nearest to position POS.\n\
633 See `find-composition' for more detail.")
634 (pos
, limit
, string
, detail_p
)
635 Lisp_Object pos
, limit
, string
, detail_p
;
637 Lisp_Object prop
, tail
;
641 CHECK_NUMBER_COERCE_MARKER (pos
, 0);
645 CHECK_NUMBER_COERCE_MARKER (limit
, 1);
651 CHECK_STRING (string
, 2);
653 if (!find_composition (start
, end
, &start
, &end
, &prop
, string
))
655 if (!COMPOSITION_VALID_P (start
, end
, prop
))
656 return Fcons (make_number (start
), Fcons (make_number (end
),
657 Fcons (Qnil
, Qnil
)));
659 return Fcons (make_number (start
), Fcons (make_number (end
),
662 if (COMPOSITION_REGISTERD_P (prop
))
663 id
= COMPOSITION_ID (prop
);
666 int start_byte
= (NILP (string
)
667 ? CHAR_TO_BYTE (start
)
668 : string_char_to_byte (string
, start
));
669 id
= get_composition_id (start
, start_byte
, end
- start
, prop
, string
);
674 Lisp_Object components
, relative_p
, mod_func
;
675 enum composition_method method
= COMPOSITION_METHOD (prop
);
676 int width
= composition_table
[id
]->width
;
678 components
= Fcopy_sequence (COMPOSITION_COMPONENTS (prop
));
679 relative_p
= (method
== COMPOSITION_WITH_RULE_ALTCHARS
681 mod_func
= COMPOSITION_MODIFICATION_FUNC (prop
);
682 tail
= Fcons (components
,
685 Fcons (make_number (width
), Qnil
))));
690 return Fcons (make_number (start
), Fcons (make_number (end
), tail
));
697 Qcomposition
= intern ("composition");
698 staticpro (&Qcomposition
);
700 /* Make a hash table for composition. */
703 extern Lisp_Object QCsize
;
707 args
[2] = QCweakness
;
710 args
[5] = make_number (311);
711 composition_hash_table
= Fmake_hash_table (6, args
);
712 staticpro (&composition_hash_table
);
715 /* Text property `composition' should be nonsticky by default. */
716 Vtext_property_default_nonsticky
717 = Fcons (Fcons (Qcomposition
, Qt
), Vtext_property_default_nonsticky
);
719 DEFVAR_LISP ("compose-chars-after-function", &Vcompose_chars_after_function
,
720 "Function to adjust composition of buffer text.\n\
722 This function is called after a text with `composition' property is\n\
723 inserted or deleted to keep `composition' property of buffer text\n\
726 The function is called with two arguments FROM and TO. They specify\n\
727 the range of text of which composition should be adjusted.\n\
729 The default value is the function `compose-chars-after'.");
730 Vcompose_chars_after_function
= intern ("compose-chars-after");
732 defsubr (&Scompose_region_internal
);
733 defsubr (&Scompose_string_internal
);
734 defsubr (&Sfind_composition_internal
);