*** empty log message ***
[bpt/emacs.git] / src / composite.c
CommitLineData
ca4c9455
KH
1/* Composite sequence support.
2 Copyright (C) 1999 Electrotechnical Laboratory, JAPAN.
3 Licensed to the Free Software Foundation.
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
19the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, USA. */
21
22#include <config.h>
23#include "lisp.h"
24#include "buffer.h"
25#include "charset.h"
26#include "intervals.h"
27
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.
33
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).
43
44 We register a composition when it is displayed, or when the width
45 is required (for instance, to calculate columns).
46
47 LENGTH -- Length of the composition. This information is used to
48 check the validity of the composition.
49
50 COMPONENTS -- Character, string, vector, list, or nil.
51
52 If it is nil, characters in the text are composed relatively
53 according to their metrics in font glyphs.
54
55 If it is a character or a string, the character or characters
56 in the string are composed relatively.
57
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.
64
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
68 composition rules.
69
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.
74
75 COMPOSITION-ID --Identification number of the composition. It is
76 used as an index to composition_table for the composition.
77
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.
88
89 We use two tables to keep information about composition;
90 composition_hash_table and composition_table.
91
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.
97
98 The latter is a table of pointers to `struct composition' indexed
99 by COMPOSITION-ID. This structure keep the other information (see
100 composite.h).
101
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.
112
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
121 the composition.
122
123*/
124
125
126Lisp_Object Qcomposition;
127
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. */
131struct composition **composition_table;
132
133/* The current size of `composition_table'. */
134static int composition_table_size;
135
136/* Number of compositions currently made. */
137int n_compositions;
138
139/* Hash table for compositions. The key is COMPONENTS-VEC of
140 `composition' property. The value is the corresponding
141 COMPOSITION-ID. */
142Lisp_Object composition_hash_table;
143
144/* Function to call to adjust composition. */
145Lisp_Object Vcompose_chars_after_function;
146
147/* Temporary variable used in macros COMPOSITION_XXX. */
148Lisp_Object composition_temp;
149\f
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)))
155
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)
163
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.
168
169 If the composition is invalid, return -1. */
170
171int
172get_composition_id (charpos, bytepos, nchars, prop, string)
173 int charpos, bytepos, nchars;
174 Lisp_Object prop, string;
175{
176 Lisp_Object id, length, components, key, *key_contents;
177 int glyph_len;
178 struct Lisp_Hash_Table *hash_table = XHASH_TABLE (composition_hash_table);
179 int hash_index;
180 unsigned hash_code;
181 struct composition *cmp;
182 int i, ch;
183
184 /* PROP should be
185 Form-A: ((LENGTH . COMPONENTS) . MODIFICATION-FUNC)
186 or
187 Form-B: (COMPOSITION-ID . (LENGTH COMPONENTS-VEC . MODIFICATION-FUNC))
188 */
189 if (nchars == 0 || !CONSP (prop))
190 goto invalid_composition;
191
192 id = XCAR (prop);
193 if (INTEGERP (id))
194 {
195 /* PROP should be Form-B. */
196 if (XINT (id) < 0 || XINT (id) >= n_compositions)
197 goto invalid_composition;
198 return XINT (id);
199 }
200
201 /* PROP should be Form-A.
202 Thus, ID should be (LENGTH . COMPONENTS). */
203 if (!CONSP (id))
204 goto invalid_composition;
205 length = XCAR (id);
206 if (!INTEGERP (length) || XINT (length) != nchars)
207 goto invalid_composition;
208
209 components = XCDR (id);
210
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))
220 key = components;
221 else if (NILP (components))
222 {
223 key = Fmake_vector (make_number (nchars), Qnil);
224 if (STRINGP (string))
225 for (i = 0; i < nchars; i++)
226 {
227 FETCH_STRING_CHAR_ADVANCE (ch, string, charpos, bytepos);
228 XVECTOR (key)->contents[i] = make_number (ch);
229 }
230 else
231 for (i = 0; i < nchars; i++)
232 {
233 FETCH_CHAR_ADVANCE (ch, charpos, bytepos);
234 XVECTOR (key)->contents[i] = make_number (ch);
235 }
236 }
237 else
238 goto invalid_composition;
239
240 hash_index = hash_lookup (hash_table, key, &hash_code);
241 if (hash_index >= 0)
242 {
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);
249 XCAR (prop) = id;
250 XCDR (prop) = Fcons (make_number (nchars), Fcons (key, XCDR (prop)));
251 return XINT (id);
252 }
253
254 /* This composition is a new one. We must register it. */
255
256 /* Check if we have sufficient memory to store this information. */
257 if (composition_table_size == 0)
258 {
259 composition_table_size = 256;
260 composition_table
261 = (struct composition **) xmalloc (sizeof (composition_table[0])
262 * composition_table_size);
263 }
264 else if (composition_table_size <= n_compositions)
265 {
266 composition_table_size += 256;
267 composition_table
268 = (struct composition **) xrealloc (composition_table,
269 sizeof (composition_table[0])
270 * composition_table_size);
271 }
272
273 key_contents = XVECTOR (key)->contents;
274
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))
279 {
280 int len = XVECTOR (key)->size;
281
282 /* The number of elements should be odd. */
283 if ((len % 2) == 0)
284 goto invalid_composition;
285 /* All elements should be integers (character or encoded
286 composition rule). */
287 for (i = 0; i < len; i++)
288 {
289 if (!INTEGERP (key_contents[i]))
290 goto invalid_composition;
291 }
292 }
293
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);
297 XCAR (prop) = id;
298 XCDR (prop) = Fcons (make_number (nchars), Fcons (key, XCDR (prop)));
299
300 /* Register the composition in composition_hash_table. */
301 hash_index = hash_put (hash_table, key, id, hash_code);
302
303 /* Register the composition in composition_table. */
304 cmp = (struct composition *) xmalloc (sizeof (struct composition));
305
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);
317 cmp->font = NULL;
318
319 /* Calculate the width of overall glyphs of the composition. */
320 if (cmp->method != COMPOSITION_WITH_RULE_ALTCHARS)
321 {
322 /* Relative composition. */
323 cmp->width = 0;
324 for (i = 0; i < glyph_len; i++)
325 {
326 int this_width;
327 ch = XINT (key_contents[i]);
328 this_width = CHAR_WIDTH (ch);
329 if (cmp->width < this_width)
330 cmp->width = this_width;
331 }
332 }
333 else
334 {
335 /* Rule-base composition. */
336 float leftmost = 0.0, rightmost;
337
338 ch = XINT (key_contents[0]);
339 rightmost = CHAR_WIDTH (ch);
340
341 for (i = 1; i < glyph_len; i += 2)
342 {
343 int rule, gref, nref;
344 int this_width;
345 float this_left;
346
347 rule = XINT (key_contents[i]);
348 ch = XINT (key_contents[i + 1]);
349 this_width = CHAR_WIDTH (ch);
350
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
354 below:
355 0---1---2 -- ascent
356 | |
357 | |
358 | |
359 9--10--11 -- center
360 | |
361 ---3---4---5--- baseline
362 | |
363 6---7---8 -- descent
364 */
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);
369
370 if (this_left < leftmost)
371 leftmost = this_left;
372 if (this_left + this_width > rightmost)
373 rightmost = this_left + this_width;
374 }
375
376 cmp->width = rightmost - leftmost;
377 if (cmp->width < (rightmost - leftmost))
378 /* To get a ceiling integer value. */
379 cmp->width++;
380 }
381
382 composition_table[n_compositions] = cmp;
383
384 return n_compositions++;
385
386 invalid_composition:
387 /* Would it be better to remove this `composition' property? */
388 return -1;
389}
390
391\f
392/* Find a composition at or nearest to position POS of OBJECT (buffer
393 or string).
394
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.
398
399 If there's no composition at POS and LIMIT is negative, return 0.
400
401 Otherwise, search for a composition forward (LIMIT > POS) or
402 backward (LIMIT < POS). In this case, LIMIT bounds the search.
403
404 If a composition is found, set *START, *END, and *PROP as above,
405 and return 1, else return 0.
406
407 This doesn't check the validity of composition. */
408
409int
410find_composition (pos, limit, start, end, prop, object)
411 int pos, limit, *start, *end;
412 Lisp_Object *prop, object;
413{
414 Lisp_Object val;
415
416 if (get_property_and_range (pos, Qcomposition, prop, start, end, object))
417 return 1;
418
419 if (limit < 0 || limit == pos)
420 return 0;
421
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));
428 pos = XINT (val);
429 if (pos == limit)
430 return 0;
431 get_property_and_range (pos, Qcomposition, prop, start, end, object);
432 return 1;
433}
434
435/* Run a proper function to adjust the composition sitting between
436 FROM and TO with property PROP. */
437
438static void
439run_composition_function (from, to, prop)
440 int from, to;
441 Lisp_Object prop;
442{
443 Lisp_Object func, val;
444 int start, end;
445
446 func = COMPOSITION_MODIFICATION_FUNC (prop);
447 /* If an invalid composition precedes or follows, try to make them
448 valid too. */
449 if (from > BEGV
450 && find_composition (from - 1, -1, &start, &end, &prop, Qnil)
451 && !COMPOSITION_VALID_P (start, end, prop))
452 from = start;
453 if (to < ZV
454 && find_composition (to, -1, &start, &end, &prop, Qnil)
455 && !COMPOSITION_VALID_P (start, end, prop))
456 to = end;
457 if (!NILP (func))
458 call2 (func, make_number (from), make_number (to));
09654086 459 else if (!NILP (Ffboundp (Vcompose_chars_after_function)))
ca4c9455
KH
460 call2 (Vcompose_chars_after_function,
461 make_number (from), make_number (to));
462}
463
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).
467
468 This function is called when a buffer text is changed. If the
469 change is deletion, FROM == TO. Otherwise, FROM < TO. */
470
471void
472update_compositions (from, to, check_mask)
473 int from, to;
474{
475 Lisp_Object prop, hook;
476 int start, end;
477
478 if (check_mask & CHECK_HEAD)
479 {
480 /* FROM should be at composition boundary. But, insertion or
481 deletion will make two compositions adjacent and
482 indistinguishable when they have same (eq) property. To
483 avoid it, in such a case, we change the property of the
484 latter to the copy of it. */
485 if (from > BEGV
486 && find_composition (from - 1, -1, &start, &end, &prop, Qnil))
487 {
488 if (from < end)
489 Fput_text_property (make_number (from), make_number (end),
490 Qcomposition,
491 Fcons (XCAR (prop), XCDR (prop)), Qnil);
492 run_composition_function (start, end, prop);
493 from = end;
494 }
495 else if (from < end
496 && find_composition (from, -1, &start, &from, &prop, Qnil))
497 run_composition_function (start, from, prop);
498 }
499
500 if (check_mask & CHECK_INSIDE)
501 {
502 /* In this case, we are sure that (check & CHECK_TAIL) is also
503 nonzero. Thus, here we should check only compositions before
504 (to - 1). */
505 while (from < to - 1
506 && find_composition (from, to, &start, &from, &prop, Qnil)
507 && from < to - 1)
508 run_composition_function (start, from, prop);
509 }
510
511 if (check_mask & CHECK_TAIL)
512 {
513 if (from < to
514 && find_composition (to - 1, -1, &start, &end, &prop, Qnil))
515 {
516 /* TO should be also at composition boundary. But,
517 insertion or deletion will make two compositions adjacent
518 and indistinguishable when they have same (eq) property.
519 To avoid it, in such a case, we change the property of
520 the former to the copy of it. */
521 if (to < end)
522 Fput_text_property (make_number (start), make_number (to),
523 Qcomposition,
524 Fcons (XCAR (prop), XCDR (prop)), Qnil);
525 run_composition_function (start, end, prop);
526 }
527 else if (to < ZV
528 && find_composition (to, -1, &start, &end, &prop, Qnil))
529 run_composition_function (start, end, prop);
530 }
531}
532
533/* Make text in the region between START and END a composition that
534 has COMPONENTS and MODIFICATION-FUNC.
535
536 If STRING is non-nil, then operate on characters contained between
537 indices START and END in STRING. */
538
539void
540compose_text (start, end, components, modification_func, string)
541 int start, end;
542 Lisp_Object components, modification_func, string;
543{
544 Lisp_Object prop;
545
546 prop = Fcons (Fcons (make_number (end - start), components),
547 modification_func);
548 Fput_text_property (make_number (start), make_number (end),
549 Qcomposition, prop, string);
550}
551
552\f
553/* Emacs Lisp APIs. */
554
555DEFUN ("compose-region-internal", Fcompose_region_internal,
556 Scompose_region_internal, 2, 4, 0,
557 "Internal use only.\n\
558\n\
559Compose text in the region between START and END.\n\
560Optional 3rd and 4th arguments are COMPONENTS and MODIFICATION-FUNC\n\
561for the composition. See `compose-region' for more detial.")
562 (start, end, components, mod_func)
563 Lisp_Object start, end, components, mod_func;
564{
565 validate_region (&start, &end);
566 if (!NILP (components)
567 && !INTEGERP (components)
568 && !CONSP (components)
569 && !STRINGP (components))
570 CHECK_VECTOR (components, 2);
571
572 compose_text (XINT (start), XINT (end), components, mod_func, Qnil);
573 return Qnil;
574}
575
576DEFUN ("compose-string-internal", Fcompose_string_internal,
577 Scompose_string_internal, 3, 5, 0,
578 "Internal use only.\n\
579\n\
580Compose text between indices START and END of STRING.\n\
581Optional 4th and 5th arguments are COMPONENTS and MODIFICATION-FUNC\n\
582for the composition. See `compose-string' for more detial.")
583 (string, start, end, components, mod_func)
584 Lisp_Object string, start, end, components, mod_func;
585{
586 CHECK_STRING (string, 0);
587 CHECK_NUMBER (start, 1);
588 CHECK_NUMBER (end, 2);
589
590 if (XINT (start) < 0 ||
591 XINT (start) > XINT (end)
592 || XINT (end) > XSTRING (string)->size)
593 args_out_of_range (start, end);
594
595 compose_text (XINT (start), XINT (end), components, mod_func, string);
596 return string;
597}
598
599DEFUN ("find-composition-internal", Ffind_composition_internal,
600 Sfind_composition_internal, 4, 4, 0,
601 "Internal use only.\n\
602\n\
603Return information about composition at or nearest to position POS.\n\
604See `find-composition' for more detail.")
605 (pos, limit, string, detail_p)
606 Lisp_Object pos, limit, string, detail_p;
607{
608 Lisp_Object prop, tail;
609 int start, end;
610 int id;
611
612 CHECK_NUMBER_COERCE_MARKER (pos, 0);
613 start = XINT (pos);
614 if (!NILP (limit))
615 {
616 CHECK_NUMBER_COERCE_MARKER (limit, 1);
617 end = XINT (limit);
618 }
619 else
620 end = -1;
621 if (!NILP (string))
622 CHECK_STRING (string, 2);
623
624 if (!find_composition (start, end, &start, &end, &prop, string))
625 return Qnil;
626 if (!COMPOSITION_VALID_P (start, end, prop))
627 return Fcons (make_number (start), Fcons (make_number (end),
628 Fcons (Qnil, Qnil)));
629 if (NILP (detail_p))
630 return Fcons (make_number (start), Fcons (make_number (end),
631 Fcons (Qt, Qnil)));
632
633 if (COMPOSITION_REGISTERD_P (prop))
634 id = COMPOSITION_ID (prop);
635 else
636 {
637 int start_byte = (NILP (string)
638 ? CHAR_TO_BYTE (start)
639 : string_char_to_byte (string, start));
640 id = get_composition_id (start, start_byte, end - start, prop, string);
641 }
642
643 if (id >= 0)
644 {
645 Lisp_Object components, relative_p, mod_func;
646 enum composition_method method = COMPOSITION_METHOD (prop);
647 int width = composition_table[id]->width;
648
649 components = Fcopy_sequence (COMPOSITION_COMPONENTS (prop));
650 relative_p = (method == COMPOSITION_WITH_RULE_ALTCHARS
651 ? Qnil : Qt);
652 mod_func = COMPOSITION_MODIFICATION_FUNC (prop);
653 tail = Fcons (components,
654 Fcons (relative_p,
655 Fcons (mod_func,
656 Fcons (make_number (width), Qnil))));
657 }
658 else
659 tail = Qnil;
660
661 return Fcons (make_number (start), Fcons (make_number (end), tail));
662}
663
664\f
665void
666syms_of_composite ()
667{
668 Qcomposition = intern ("composition");
669 staticpro (&Qcomposition);
670
671 /* Make a hash table for composition. */
672 {
09654086 673 Lisp_Object args[6];
ca4c9455
KH
674 extern Lisp_Object QCsize;
675
676 args[0] = QCtest;
677 args[1] = Qequal;
678 args[2] = QCweakness;
679 args[3] = Qnil;
680 args[4] = QCsize;
681 args[5] = make_number (311);
09654086 682 composition_hash_table = Fmake_hash_table (6, args);
ca4c9455
KH
683 staticpro (&composition_hash_table);
684 }
685
686 /* Text property `composition' should be nonsticky by default. */
687 Vtext_property_default_nonsticky
688 = Fcons (Fcons (Qcomposition, Qt), Vtext_property_default_nonsticky);
689
690 DEFVAR_LISP ("compose-chars-after-function", &Vcompose_chars_after_function,
691 "Function to adjust composition of buffer text.\n\
692\n\
693This function is called after a text with `composition' property is\n\
694inserted or deleted to keep `composition' property of buffer text\n\
695valid.\n\
696\n\
697The function is called with two arguments FROM and TO. They specify\n\
698the range of text of which composition should be adjusted.\n\
699\n\
700The default value is the function `compose-chars-after'.");
701 Vcompose_chars_after_function = intern ("compose-chars-after");
702
703 defsubr (&Scompose_region_internal);
704 defsubr (&Scompose_string_internal);
705 defsubr (&Sfind_composition_internal);
706}