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 */