Mercurial > emacs
changeset 26848:d502b027b817
New file
author | Kenichi Handa <handa@m17n.org> |
---|---|
date | Wed, 15 Dec 1999 00:08:01 +0000 |
parents | 2f17ea330dae |
children | a0ecda172035 |
files | src/composite.c src/composite.h |
diffstat | 2 files changed, 914 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/composite.c Wed Dec 15 00:08:01 1999 +0000 @@ -0,0 +1,707 @@ +/* Composite sequence support. + Copyright (C) 1999 Electrotechnical Laboratory, JAPAN. + Licensed to the Free Software Foundation. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include <config.h> +#include "lisp.h" +#include "buffer.h" +#include "charset.h" +#include "intervals.h" + +/* Emacs uses special text property `composition' to support character + composition. A sequence of characters that have the same (i.e. eq) + `composition' property value is treated as a single composite + sequence (we call it just `composition' here after). Characters in + a composition are all composed somehow on the screen. + + The property value has this form when the composition is made: + ((LENGTH . COMPONENTS) . MODIFICATION-FUNC) + then turns to this form: + (COMPOSITION-ID . (LENGTH COMPONENTS-VEC . MODIFICATION-FUNC)) + when the composition is registered in composition_hash_table and + composition_table. These rather peculiar structures were designed + to make it easy to distinguish them quickly (we can do that by + checking only the first element) and to extract LENGTH (from the + former form) and COMPOSITION-ID (from the latter form). + + We register a composition when it is displayed, or when the width + is required (for instance, to calculate columns). + + LENGTH -- Length of the composition. This information is used to + check the validity of the composition. + + COMPONENTS -- Character, string, vector, list, or nil. + + If it is nil, characters in the text are composed relatively + according to their metrics in font glyphs. + + If it is a character or a string, the character or characters + in the string are composed relatively. + + If it is a vector or list of integers, the element is a + character or an encoded composition rule. The characters are + composed according to the rules. (2N)th elements are + characters to be composed and (2N+1)th elements are + composition rules to tell how to compose (2N+2)th element with + the previously composed 2N glyphs. + + COMPONENTS-VEC -- Vector of integers. In relative composition, the + elements are characters to be composed. In rule-base + composition, the elements are characters or encoded + composition rules. + + MODIFICATION-FUNC -- If non nil, it is a function to call when the + composition gets invalid after a modification in a buffer. If + it is nil, a function in `composition-function-table' of the + first character in the sequence is called. + + COMPOSITION-ID --Identification number of the composition. It is + used as an index to composition_table for the composition. + + When Emacs has to display a composition or has to know its + displaying width, the function get_composition_id is called. It + returns COMPOSITION-ID so that the caller can access the + information about the composition through composition_table. If a + COMPOSITION-ID has not yet been assigned to the composition, + get_composition_id checks the validity of `composition' property, + and, if valid, assigns a new ID, registers the information in + composition_hash_table and composition_table, and changes the form + of the property value. If the property is invalid, return -1 + without changing the property value. + + We use two tables to keep information about composition; + composition_hash_table and composition_table. + + The former is a hash table in which keys are COMPONENTS-VECs and + values are the corresponding COMPOSITION-IDs. This hash table is + weak, but as each key (COMPONENTS-VEC) is also kept as a value of + `composition' property, it won't be collected as garbage until all + text that have the same COMPONENTS-VEC are deleted. + + The latter is a table of pointers to `struct composition' indexed + by COMPOSITION-ID. This structure keep the other information (see + composite.h). + + In general, a text property holds information about individual + characters. But, a `composition' property holds information about + a sequence of characters (in this sense, it is like `intangible' + property). That means that we should not share the property value + in adjacent compositions we can't distinguish them if they have the + same property. So, after any changes, we call + `update_compositions' and change a property of one of adjacent + compositions to a copy of it. This function also runs a proper + composition modification function to make a composition that gets + invalid by the change valid again. + + As a value of `composition' property holds information about a + specific range of text, the value gets invalid if we change the + text in the range. We treat `composition' property always + rear-nonsticky (currently by setting default-text-properties to + (rear-nonsticky (composition))) and we never make properties of + adjacent compositions identical. Thus, any such changes make the + range just shorter. So, we can check the validity of `composition' + property by comparing LENGTH information with the actual length of + the composition. + +*/ + + +Lisp_Object Qcomposition; + +/* Table of pointers to the structure `composition' indexed by + COMPOSITION-ID. This structure is for storing information about + each composition except for COMPONENTS-VEC. */ +struct composition **composition_table; + +/* The current size of `composition_table'. */ +static int composition_table_size; + +/* Number of compositions currently made. */ +int n_compositions; + +/* Hash table for compositions. The key is COMPONENTS-VEC of + `composition' property. The value is the corresponding + COMPOSITION-ID. */ +Lisp_Object composition_hash_table; + +/* Function to call to adjust composition. */ +Lisp_Object Vcompose_chars_after_function; + +/* Temporary variable used in macros COMPOSITION_XXX. */ +Lisp_Object composition_temp; + +/* Return how many columns C will occupy on the screen. It always + returns 1 for control characters and 8-bit characters because those + are just ignored in a composition. */ +#define CHAR_WIDTH(c) \ + (SINGLE_BYTE_CHAR_P (c) ? 1 : CHARSET_WIDTH (CHAR_CHARSET (c))) + +/* The following macros for hash table are copied from fns.c. */ +/* Return the contents of vector V at index IDX. */ +#define AREF(V, IDX) XVECTOR (V)->contents[IDX] +/* Value is the key part of entry IDX in hash table H. */ +#define HASH_KEY(H, IDX) AREF ((H)->key_and_value, 2 * (IDX)) +/* Value is the value part of entry IDX in hash table H. */ +#define HASH_VALUE(H, IDX) AREF ((H)->key_and_value, 2 * (IDX) + 1) + +/* Return COMPOSITION-ID of a composition at buffer position + CHARPOS/BYTEPOS and length NCHARS. The `composition' property of + the sequence is PROP. STRING, if non-nil, is a string that + contains the composition instead of the current buffer. + + If the composition is invalid, return -1. */ + +int +get_composition_id (charpos, bytepos, nchars, prop, string) + int charpos, bytepos, nchars; + Lisp_Object prop, string; +{ + Lisp_Object id, length, components, key, *key_contents; + int glyph_len; + struct Lisp_Hash_Table *hash_table = XHASH_TABLE (composition_hash_table); + int hash_index; + unsigned hash_code; + struct composition *cmp; + int i, ch; + + /* PROP should be + Form-A: ((LENGTH . COMPONENTS) . MODIFICATION-FUNC) + or + Form-B: (COMPOSITION-ID . (LENGTH COMPONENTS-VEC . MODIFICATION-FUNC)) + */ + if (nchars == 0 || !CONSP (prop)) + goto invalid_composition; + + id = XCAR (prop); + if (INTEGERP (id)) + { + /* PROP should be Form-B. */ + if (XINT (id) < 0 || XINT (id) >= n_compositions) + goto invalid_composition; + return XINT (id); + } + + /* PROP should be Form-A. + Thus, ID should be (LENGTH . COMPONENTS). */ + if (!CONSP (id)) + goto invalid_composition; + length = XCAR (id); + if (!INTEGERP (length) || XINT (length) != nchars) + goto invalid_composition; + + components = XCDR (id); + + /* Check if the same composition has already been registered or not + by consulting composition_hash_table. The key for this table is + COMPONENTS (converted to a vector COMPONENTS-VEC) or, if it is + nil, vector of characters in the composition range. */ + if (INTEGERP (components)) + key = Fmake_vector (make_number (1), components); + else if (STRINGP (components) || CONSP (components)) + key = Fvconcat (1, &components); + else if (VECTORP (components)) + key = components; + else if (NILP (components)) + { + key = Fmake_vector (make_number (nchars), Qnil); + if (STRINGP (string)) + for (i = 0; i < nchars; i++) + { + FETCH_STRING_CHAR_ADVANCE (ch, string, charpos, bytepos); + XVECTOR (key)->contents[i] = make_number (ch); + } + else + for (i = 0; i < nchars; i++) + { + FETCH_CHAR_ADVANCE (ch, charpos, bytepos); + XVECTOR (key)->contents[i] = make_number (ch); + } + } + else + goto invalid_composition; + + hash_index = hash_lookup (hash_table, key, &hash_code); + if (hash_index >= 0) + { + /* We have already registered the same composition. Change PROP + from Form-A above to Form-B while replacing COMPONENTS with + COMPONENTS-VEC stored in the hash table. We can directly + modify the cons cell of PROP because it is not shared. */ + key = HASH_KEY (hash_table, hash_index); + id = HASH_VALUE (hash_table, hash_index); + XCAR (prop) = id; + XCDR (prop) = Fcons (make_number (nchars), Fcons (key, XCDR (prop))); + return XINT (id); + } + + /* This composition is a new one. We must register it. */ + + /* Check if we have sufficient memory to store this information. */ + if (composition_table_size == 0) + { + composition_table_size = 256; + composition_table + = (struct composition **) xmalloc (sizeof (composition_table[0]) + * composition_table_size); + } + else if (composition_table_size <= n_compositions) + { + composition_table_size += 256; + composition_table + = (struct composition **) xrealloc (composition_table, + sizeof (composition_table[0]) + * composition_table_size); + } + + key_contents = XVECTOR (key)->contents; + + /* Check if the contents of COMPONENTS are valid if COMPONENTS is a + vector or a list. It should be a sequence of: + char1 rule1 char2 rule2 char3 ... ruleN charN+1 */ + if (VECTORP (components) || CONSP (components)) + { + int len = XVECTOR (key)->size; + + /* The number of elements should be odd. */ + if ((len % 2) == 0) + goto invalid_composition; + /* All elements should be integers (character or encoded + composition rule). */ + for (i = 0; i < len; i++) + { + if (!INTEGERP (key_contents[i])) + goto invalid_composition; + } + } + + /* Change PROP from Form-A above to Form-B. We can directly modify + the cons cell of PROP because it is not shared. */ + XSETFASTINT (id, n_compositions); + XCAR (prop) = id; + XCDR (prop) = Fcons (make_number (nchars), Fcons (key, XCDR (prop))); + + /* Register the composition in composition_hash_table. */ + hash_index = hash_put (hash_table, key, id, hash_code); + + /* Register the composition in composition_table. */ + cmp = (struct composition *) xmalloc (sizeof (struct composition)); + + cmp->method = (NILP (components) + ? COMPOSITION_RELATIVE + : ((INTEGERP (components) || STRINGP (components)) + ? COMPOSITION_WITH_ALTCHARS + : COMPOSITION_WITH_RULE_ALTCHARS)); + cmp->hash_index = hash_index; + glyph_len = (cmp->method == COMPOSITION_WITH_RULE_ALTCHARS + ? (XVECTOR (key)->size + 1) / 2 + : XVECTOR (key)->size); + cmp->glyph_len = glyph_len; + cmp->offsets = (short *) xmalloc (sizeof (short) * glyph_len * 2); + cmp->font = NULL; + + /* Calculate the width of overall glyphs of the composition. */ + if (cmp->method != COMPOSITION_WITH_RULE_ALTCHARS) + { + /* Relative composition. */ + cmp->width = 0; + for (i = 0; i < glyph_len; i++) + { + int this_width; + ch = XINT (key_contents[i]); + this_width = CHAR_WIDTH (ch); + if (cmp->width < this_width) + cmp->width = this_width; + } + } + else + { + /* Rule-base composition. */ + float leftmost = 0.0, rightmost; + + ch = XINT (key_contents[0]); + rightmost = CHAR_WIDTH (ch); + + for (i = 1; i < glyph_len; i += 2) + { + int rule, gref, nref; + int this_width; + float this_left; + + rule = XINT (key_contents[i]); + ch = XINT (key_contents[i + 1]); + this_width = CHAR_WIDTH (ch); + + /* A composition rule is specified by an integer value + that encodes global and new reference points (GREF and + NREF). GREF and NREF are specified by numbers as + below: + 0---1---2 -- ascent + | | + | | + | | + 9--10--11 -- center + | | + ---3---4---5--- baseline + | | + 6---7---8 -- descent + */ + COMPOSITION_DECODE_RULE (rule, gref, nref); + this_left = (leftmost + + (gref % 3) * (rightmost - leftmost) / 2.0 + - (nref % 3) * this_width / 2.0); + + if (this_left < leftmost) + leftmost = this_left; + if (this_left + this_width > rightmost) + rightmost = this_left + this_width; + } + + cmp->width = rightmost - leftmost; + if (cmp->width < (rightmost - leftmost)) + /* To get a ceiling integer value. */ + cmp->width++; + } + + composition_table[n_compositions] = cmp; + + return n_compositions++; + + invalid_composition: + /* Would it be better to remove this `composition' property? */ + return -1; +} + + +/* Find a composition at or nearest to position POS of OBJECT (buffer + or string). + + OBJECT defaults to the current buffer. If there's a composition at + POS, set *START and *END to the start and end of the sequence, + *PROP to the `composition' property, and return 1. + + If there's no composition at POS and LIMIT is negative, return 0. + + Otherwise, search for a composition forward (LIMIT > POS) or + backward (LIMIT < POS). In this case, LIMIT bounds the search. + + If a composition is found, set *START, *END, and *PROP as above, + and return 1, else return 0. + + This doesn't check the validity of composition. */ + +int +find_composition (pos, limit, start, end, prop, object) + int pos, limit, *start, *end; + Lisp_Object *prop, object; +{ + Lisp_Object val; + + if (get_property_and_range (pos, Qcomposition, prop, start, end, object)) + return 1; + + if (limit < 0 || limit == pos) + return 0; + + if (limit > pos) /* search forward */ + val = Fnext_single_property_change (make_number (pos), Qcomposition, + object, make_number (limit)); + else /* search backward */ + val = Fprevious_single_property_change (make_number (pos), Qcomposition, + object, make_number (limit)); + pos = XINT (val); + if (pos == limit) + return 0; + get_property_and_range (pos, Qcomposition, prop, start, end, object); + return 1; +} + +/* Run a proper function to adjust the composition sitting between + FROM and TO with property PROP. */ + +static void +run_composition_function (from, to, prop) + int from, to; + Lisp_Object prop; +{ + Lisp_Object func, val; + int start, end; + + func = COMPOSITION_MODIFICATION_FUNC (prop); + /* If an invalid composition precedes or follows, try to make them + valid too. */ + if (from > BEGV + && find_composition (from - 1, -1, &start, &end, &prop, Qnil) + && !COMPOSITION_VALID_P (start, end, prop)) + from = start; + if (to < ZV + && find_composition (to, -1, &start, &end, &prop, Qnil) + && !COMPOSITION_VALID_P (start, end, prop)) + to = end; + if (!NILP (func)) + call2 (func, make_number (from), make_number (to)); + else if (Ffboundp (Vcompose_chars_after_function)) + call2 (Vcompose_chars_after_function, + make_number (from), make_number (to)); +} + +/* Make invalid compositions adjacent to or inside FROM and TO valid. + CHECK_MASK is bitwise `or' of mask bits defined by macros + CHECK_XXX (see the comment in composite.h). + + This function is called when a buffer text is changed. If the + change is deletion, FROM == TO. Otherwise, FROM < TO. */ + +void +update_compositions (from, to, check_mask) + int from, to; +{ + Lisp_Object prop, hook; + int start, end; + + if (check_mask & CHECK_HEAD) + { + /* FROM should be at composition boundary. But, insertion or + deletion will make two compositions adjacent and + indistinguishable when they have same (eq) property. To + avoid it, in such a case, we change the property of the + latter to the copy of it. */ + if (from > BEGV + && find_composition (from - 1, -1, &start, &end, &prop, Qnil)) + { + if (from < end) + Fput_text_property (make_number (from), make_number (end), + Qcomposition, + Fcons (XCAR (prop), XCDR (prop)), Qnil); + run_composition_function (start, end, prop); + from = end; + } + else if (from < end + && find_composition (from, -1, &start, &from, &prop, Qnil)) + run_composition_function (start, from, prop); + } + + if (check_mask & CHECK_INSIDE) + { + /* In this case, we are sure that (check & CHECK_TAIL) is also + nonzero. Thus, here we should check only compositions before + (to - 1). */ + while (from < to - 1 + && find_composition (from, to, &start, &from, &prop, Qnil) + && from < to - 1) + run_composition_function (start, from, prop); + } + + if (check_mask & CHECK_TAIL) + { + if (from < to + && find_composition (to - 1, -1, &start, &end, &prop, Qnil)) + { + /* TO should be also at composition boundary. But, + insertion or deletion will make two compositions adjacent + and indistinguishable when they have same (eq) property. + To avoid it, in such a case, we change the property of + the former to the copy of it. */ + if (to < end) + Fput_text_property (make_number (start), make_number (to), + Qcomposition, + Fcons (XCAR (prop), XCDR (prop)), Qnil); + run_composition_function (start, end, prop); + } + else if (to < ZV + && find_composition (to, -1, &start, &end, &prop, Qnil)) + run_composition_function (start, end, prop); + } +} + +/* Make text in the region between START and END a composition that + has COMPONENTS and MODIFICATION-FUNC. + + If STRING is non-nil, then operate on characters contained between + indices START and END in STRING. */ + +void +compose_text (start, end, components, modification_func, string) + int start, end; + Lisp_Object components, modification_func, string; +{ + Lisp_Object prop; + + prop = Fcons (Fcons (make_number (end - start), components), + modification_func); + Fput_text_property (make_number (start), make_number (end), + Qcomposition, prop, string); +} + + +/* Emacs Lisp APIs. */ + +DEFUN ("compose-region-internal", Fcompose_region_internal, + Scompose_region_internal, 2, 4, 0, + "Internal use only.\n\ +\n\ +Compose text in the region between START and END.\n\ +Optional 3rd and 4th arguments are COMPONENTS and MODIFICATION-FUNC\n\ +for the composition. See `compose-region' for more detial.") + (start, end, components, mod_func) + Lisp_Object start, end, components, mod_func; +{ + validate_region (&start, &end); + if (!NILP (components) + && !INTEGERP (components) + && !CONSP (components) + && !STRINGP (components)) + CHECK_VECTOR (components, 2); + + compose_text (XINT (start), XINT (end), components, mod_func, Qnil); + return Qnil; +} + +DEFUN ("compose-string-internal", Fcompose_string_internal, + Scompose_string_internal, 3, 5, 0, + "Internal use only.\n\ +\n\ +Compose text between indices START and END of STRING.\n\ +Optional 4th and 5th arguments are COMPONENTS and MODIFICATION-FUNC\n\ +for the composition. See `compose-string' for more detial.") + (string, start, end, components, mod_func) + Lisp_Object string, start, end, components, mod_func; +{ + CHECK_STRING (string, 0); + CHECK_NUMBER (start, 1); + CHECK_NUMBER (end, 2); + + if (XINT (start) < 0 || + XINT (start) > XINT (end) + || XINT (end) > XSTRING (string)->size) + args_out_of_range (start, end); + + compose_text (XINT (start), XINT (end), components, mod_func, string); + return string; +} + +DEFUN ("find-composition-internal", Ffind_composition_internal, + Sfind_composition_internal, 4, 4, 0, + "Internal use only.\n\ +\n\ +Return information about composition at or nearest to position POS.\n\ +See `find-composition' for more detail.") + (pos, limit, string, detail_p) + Lisp_Object pos, limit, string, detail_p; +{ + Lisp_Object prop, tail; + int start, end; + int id; + + CHECK_NUMBER_COERCE_MARKER (pos, 0); + start = XINT (pos); + if (!NILP (limit)) + { + CHECK_NUMBER_COERCE_MARKER (limit, 1); + end = XINT (limit); + } + else + end = -1; + if (!NILP (string)) + CHECK_STRING (string, 2); + + if (!find_composition (start, end, &start, &end, &prop, string)) + return Qnil; + if (!COMPOSITION_VALID_P (start, end, prop)) + return Fcons (make_number (start), Fcons (make_number (end), + Fcons (Qnil, Qnil))); + if (NILP (detail_p)) + return Fcons (make_number (start), Fcons (make_number (end), + Fcons (Qt, Qnil))); + + if (COMPOSITION_REGISTERD_P (prop)) + id = COMPOSITION_ID (prop); + else + { + int start_byte = (NILP (string) + ? CHAR_TO_BYTE (start) + : string_char_to_byte (string, start)); + id = get_composition_id (start, start_byte, end - start, prop, string); + } + + if (id >= 0) + { + Lisp_Object components, relative_p, mod_func; + enum composition_method method = COMPOSITION_METHOD (prop); + int width = composition_table[id]->width; + + components = Fcopy_sequence (COMPOSITION_COMPONENTS (prop)); + relative_p = (method == COMPOSITION_WITH_RULE_ALTCHARS + ? Qnil : Qt); + mod_func = COMPOSITION_MODIFICATION_FUNC (prop); + tail = Fcons (components, + Fcons (relative_p, + Fcons (mod_func, + Fcons (make_number (width), Qnil)))); + } + else + tail = Qnil; + + return Fcons (make_number (start), Fcons (make_number (end), tail)); +} + + +void +syms_of_composite () +{ + Qcomposition = intern ("composition"); + staticpro (&Qcomposition); + + /* Make a hash table for composition. */ + { + Lisp_Object args[6], nargs; + extern Lisp_Object QCsize; + + args[0] = QCtest; + args[1] = Qequal; + args[2] = QCweakness; + args[3] = Qnil; + args[4] = QCsize; + args[5] = make_number (311); + XSETINT (nargs, 6); + composition_hash_table = Fmake_hash_table (nargs, args); + staticpro (&composition_hash_table); + } + + /* Text property `composition' should be nonsticky by default. */ + Vtext_property_default_nonsticky + = Fcons (Fcons (Qcomposition, Qt), Vtext_property_default_nonsticky); + + DEFVAR_LISP ("compose-chars-after-function", &Vcompose_chars_after_function, + "Function to adjust composition of buffer text.\n\ +\n\ +This function is called after a text with `composition' property is\n\ +inserted or deleted to keep `composition' property of buffer text\n\ +valid.\n\ +\n\ +The function is called with two arguments FROM and TO. They specify\n\ +the range of text of which composition should be adjusted.\n\ +\n\ +The default value is the function `compose-chars-after'."); + Vcompose_chars_after_function = intern ("compose-chars-after"); + + defsubr (&Scompose_region_internal); + defsubr (&Scompose_string_internal); + defsubr (&Sfind_composition_internal); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/composite.h Wed Dec 15 00:08:01 1999 +0000 @@ -0,0 +1,207 @@ +/* Header for composite sequence handler. + Copyright (C) 1999 Electrotechnical Laboratory, JAPAN. + Licensed to the Free Software Foundation. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifndef _COMPOSITE_H +#define _COMPOSITE_H + +/* Methods to display a sequence of components a composition. */ +enum composition_method { + /* The first two are actually not methods, but used in code + conversion to specify the current composing status. */ + COMPOSITION_DISABLED, /* Never handle composition data */ + COMPOSITION_NO, /* Not processing composition data */ + /* Compose relatively without alternate characters. */ + COMPOSITION_RELATIVE, + /* Compose by specified composition rule. This is not used in Emacs + 21 but we need it to decode files saved in the older versions of + Emacs. */ + COMPOSITION_WITH_RULE, + /* Compose relatively with alternate characters. */ + COMPOSITION_WITH_ALTCHARS, + /* Compose by specified composition rule with alternate characters. */ + COMPOSITION_WITH_RULE_ALTCHARS +}; + +/* Maximum number of compoments a single composition can have. */ +#define MAX_COMPOSITION_COMPONENTS 16 + +/* These macros access information about a composition that + has `composition' property PROP. PROP is: + ((LENGTH . COMPONENTS) . MODIFICATION-FUNC) + or + (COMPOSITION-ID . (LENGTH COMPONENTS . MODIFICATION-FUNC)) + They don't check validity of PROP. */ + +/* Temporary variable used only in the following macros. */ +extern Lisp_Object composition_temp; + +/* Return 1 iff the composition is already registered. */ +#define COMPOSITION_REGISTERD_P(prop) INTEGERP (XCAR (prop)) + +/* Return ID number of the already registered composition. */ +#define COMPOSITION_ID(prop) XINT (XCAR (prop)) + +/* Return length of the composition. */ +#define COMPOSITION_LENGTH(prop) \ + (COMPOSITION_REGISTERD_P (prop) \ + ? XINT (XCAR (XCDR (prop))) \ + : XINT (XCAR (XCAR (prop)))) + +/* Return components of the composition. */ +#define COMPOSITION_COMPONENTS(prop) \ + (COMPOSITION_REGISTERD_P (prop) \ + ? XCAR (XCDR (XCDR (prop))) \ + : XCDR (XCAR (prop))) + +/* Return modification function of the composition. */ +#define COMPOSITION_MODIFICATION_FUNC(prop) \ + (COMPOSITION_REGISTERD_P (prop) \ + ? XCDR (XCDR (XCDR (prop))) \ + : XCDR (prop)) + +/* Return the method of composition. */ +#define COMPOSITION_METHOD(prop) \ + (COMPOSITION_REGISTERD_P (prop) \ + ? composition_table[COMPOSITION_ID (prop)]->method \ + : (composition_temp = XCDR (XCAR (prop)), \ + (NILP (composition_temp) \ + ? COMPOSITION_RELATIVE \ + : ((INTEGERP (composition_temp) || STRINGP (composition_temp)) \ + ? COMPOSITION_WITH_ALTCHARS \ + : COMPOSITION_WITH_RULE_ALTCHARS)))) + +/* Return 1 iff the composition is valid. It is valid if length of + the composition equals to (END - START). */ +#define COMPOSITION_VALID_P(start, end, prop) \ + (CONSP (prop) \ + && (COMPOSITION_REGISTERD_P (prop) \ + ? (COMPOSITION_ID (prop) >= 0 \ + && COMPOSITION_ID (prop) <= n_compositions \ + && CONSP (XCDR (prop))) \ + : (composition_temp = XCAR (prop), \ + (CONSP (composition_temp) \ + && (composition_temp = XCDR (composition_temp), \ + (NILP (composition_temp) || STRINGP (composition_temp) \ + || VECTORP (composition_temp) || CONSP (composition_temp))))))\ + && (end - start) == COMPOSITION_LENGTH (prop)) + +/* Return the Nth glyph of composition specified by CMP. CMP is a + pointer to `struct composition'. */ +#define COMPOSITION_GLYPH(cmp, n) \ + XINT (XVECTOR (XVECTOR (XHASH_TABLE (composition_hash_table) \ + ->key_and_value) \ + ->contents[cmp->hash_index * 2]) \ + ->contents[cmp->method == COMPOSITION_WITH_RULE_ALTCHARS \ + ? (n) * 2 : (n)]) + +/* Return the encoded composition rule to compose the Nth glyph of + rule-base composition specified by CMP. CMP is a pointer to + `struct composition'. */ +#define COMPOSITION_RULE(cmp, n) \ + XINT (XVECTOR (XVECTOR (XHASH_TABLE (composition_hash_table) \ + ->key_and_value) \ + ->contents[cmp->hash_index * 2]) \ + ->contents[(n) * 2 - 1]) + +/* Decode encoded composition rule RULE_CODE into GREF (global + reference point code) and NREF (new reference point code). Don't + check RULE_CODE, always set GREF and NREF to valid values. */ +#define COMPOSITION_DECODE_RULE(rule_code, gref, nref) \ + do { \ + gref = (rule_code) / 12; \ + if (gref > 12) gref = 11; \ + nref = (rule_code) % 12; \ + } while (0) + +/* Return encoded composition rule for the pair of global reference + point GREF and new reference point NREF. If arguments are invalid, + return -1. */ +#define COMPOSITION_ENCODE_RULE(gref, nref) \ + ((unsigned) (gref) < 12 && (unsigned) (nref) < 12 \ + ? (gref) * 12 + (nref) : -1) + +/* Data structure that records information about a composition + currently used in some buffers or strings. + + When a composition is assigned an ID number (by + get_composition_id), this structure is allocated for the + composition and linked in composition_table[ID]. + + Identical compositions appearing at different places have the same + ID, and thus share the same instance of this structure. */ + +struct composition { + /* How many columns the overall glyphs occupy on the screen. This + gives an approximate value for column calculation in + Fcurrent_column, and etc. */ + unsigned char width; + + /* Number of glyphs of the composition components. */ + unsigned char glyph_len; + + /* Method of the composition. */ + enum composition_method method; + + /* Index to the composition hash table. */ + int hash_index; + + /* For which font we have calculated the remaining members. The + actual type is device dependent. */ + void *font; + + /* Pointer to an array of x-offset and y-offset (by pixels) of + glyphs. This points to a sufficient memory space (sizeof (int) * + glyph_len * 2) that is allocated when the composition is + registered in composition_table. X-offset and Y-offset of Nth + glyph are (2N)th and (2N+1)th elements respectively. */ + short *offsets; + + /* Width, ascent, and descent pixels of the composition. */ + short pixel_width, ascent, descent; + +}; + +/* Table of pointers to the structure `composition' indexed by + COMPOSITION-ID. */ +extern struct composition **composition_table; +/* Number of the currently registered compositions. */ +extern int n_compositions; + +/* Mask bits for CHECK_MASK arg to update_compositions. + For a change in the region FROM and TO, check compositions ... */ +#define CHECK_HEAD 1 /* adjacent to FROM */ +#define CHECK_TAIL 2 /* adjacent to TO */ +#define CHECK_INSIDE 4 /* between FROM and TO */ +#define CHECK_BORDER (CHECK_HEAD | CHECK_TAIL) +#define CHECK_ALL (CHECK_BORDER | CHECK_INSIDE) + +extern Lisp_Object Qcomposition; +extern Lisp_Object composition_hash_table; + +extern int get_composition_id P_ ((int, int, int, Lisp_Object, Lisp_Object)); +extern int find_composition P_ ((int, int, int *, int *, Lisp_Object *, + Lisp_Object)); +extern void update_compositions P_ ((int, int, int)); +extern void compose_region P_ ((int, int, Lisp_Object, Lisp_Object, + Lisp_Object)); +extern void syms_of_composition P_ (()); + +#endif /* not _COMPOSITE_H */