+ int from1, to1;
+
+ elt = char_table_ref_and_range (Vdefault_fontset, c, &from1, &to1);
+ if (*from < from1)
+ *from = from1;
+ if (*to > to1)
+ *to = to1;
+ }
+ return elt;
+}
+
+
+/* Set elements of FONTSET for characters in RANGE to the value ELT.
+ RANGE is a cons (FROM . TO), where FROM and TO are character codes
+ specifying a range. */
+
+#define FONTSET_SET(fontset, range, elt) \
+ Fset_char_table_range ((fontset), (range), (elt))
+
+
+/* Modify the elements of FONTSET for characters in RANGE by replacing
+ with ELT or adding ELT. RANGE is a cons (FROM . TO), where FROM
+ and TO are character codes specifying a range. If ADD is nil,
+ replace with ELT, if ADD is `prepend', prepend ELT, otherwise,
+ append ELT. */
+
+#define FONTSET_ADD(fontset, range, elt, add) \
+ (NILP (add) \
+ ? (NILP (range) \
+ ? (FONTSET_FALLBACK (fontset) = Fmake_vector (make_number (1), (elt))) \
+ : Fset_char_table_range ((fontset), (range), \
+ Fmake_vector (make_number (1), (elt)))) \
+ : fontset_add ((fontset), (range), (elt), (add)))
+
+static Lisp_Object
+fontset_add (fontset, range, elt, add)
+ Lisp_Object fontset, range, elt, add;
+{
+ Lisp_Object args[2];
+ int idx = (EQ (add, Qappend) ? 0 : 1);
+
+ args[1 - idx] = Fmake_vector (make_number (1), elt);
+
+ if (CONSP (range))
+ {
+ int from = XINT (XCAR (range));
+ int to = XINT (XCDR (range));
+ int from1, to1;
+
+ do {
+ args[idx] = char_table_ref_and_range (fontset, from, &from1, &to1);
+ if (to < to1)
+ to1 = to;
+ char_table_set_range (fontset, from, to1,
+ NILP (args[idx]) ? args[1 - idx]
+ : Fvconcat (2, args));
+ from = to1 + 1;
+ } while (from < to);
+ }
+ else
+ {
+ args[idx] = FONTSET_FALLBACK (fontset);
+ FONTSET_FALLBACK (fontset)
+ = NILP (args[idx]) ? args[1 - idx] : Fvconcat (2, args);
+ }
+ return Qnil;
+}
+
+
+/* Update FONTSET_ELEMENT which has this form:
+ [CHARSET-ORDERED-LIST-TICK PREFERRED-CHARSET-ID PREFERRED-RFONT-DEF
+ RFONT-DEF0 RFONT-DEF1 ...].
+ Reorder RFONT-DEFs according to the current order of charset
+ (Vcharset_ordered_list), and update CHARSET-ORDERED-LIST-TICK to
+ the latest value. */
+
+static void
+reorder_font_vector (fontset_element)
+ Lisp_Object fontset_element;
+{
+ Lisp_Object list, *new_vec;
+ Lisp_Object font_def;
+ int size;
+ int *charset_id_table;
+ int i, idx;
+
+ ASET (fontset_element, 0, make_number (charset_ordered_list_tick));
+ size = ASIZE (fontset_element) - 3;
+ if (size <= 1)
+ /* No need to reorder VEC. */
+ return;
+ charset_id_table = (int *) alloca (sizeof (int) * size);
+ new_vec = (Lisp_Object *) alloca (sizeof (Lisp_Object) * size);
+
+ /* At first, extract ENCODING (a chaset ID) from each FONT-DEF.
+ FONT-DEF has this form:
+ [FACE-ID FONT-INDEX [ FONT-SPEC ENCODING REPERTORY ]] */
+ for (i = 0; i < size; i++)
+ {
+ font_def = AREF (fontset_element, i + 3);
+ charset_id_table[i] = XINT (AREF (AREF (font_def, 2), 1));
+ }
+
+ /* Then, store FONT-DEFs in NEW_VEC in the correct order. */
+ for (idx = 0, list = Vcharset_ordered_list;
+ idx < size && CONSP (list); list = XCDR (list))
+ {
+ for (i = 0; i < size; i++)
+ if (charset_id_table[i] == XINT (XCAR (list)))
+ new_vec[idx++] = AREF (fontset_element, i + 3);
+ }
+
+ /* At last, update FONT-DEFs. */
+ for (i = 0; i < size; i++)
+ ASET (fontset_element, i + 3, new_vec[i]);
+}
+
+
+/* Load a font matching the font related attributes in FACE->lface and
+ font pattern in FONT_DEF of FONTSET, and return an index of the
+ font. FONT_DEF has this form:
+ [ FONT-SPEC ENCODING REPERTORY ]
+ If REPERTORY is nil, generate a char-table representing the font
+ repertory by looking into the font itself. */
+
+static int
+load_font_get_repertory (f, face, font_def, fontset)
+ FRAME_PTR f;
+ struct face *face;
+ Lisp_Object font_def;
+ Lisp_Object fontset;
+{
+ char *font_name;
+ struct font_info *font_info;
+ int charset;
+
+ font_name = choose_face_font (f, face->lface, AREF (font_def, 0), NULL);
+ charset = XINT (AREF (font_def, 1));
+ if (! (font_info = fs_load_font (f, font_name, charset)))
+ return -1;
+
+ if (NILP (AREF (font_def, 2))
+ && NILP (Fassq (make_number (font_info->font_idx),
+ FONTSET_REPERTORY (fontset))))
+ {
+ /* We must look into the font to get the correct repertory as a
+ char-table. */
+ Lisp_Object repertory;
+
+ repertory = (*get_font_repertory_func) (f, font_info);
+ FONTSET_REPERTORY (fontset)
+ = Fcons (Fcons (make_number (font_info->font_idx), repertory),
+ FONTSET_REPERTORY (fontset));
+ }
+
+ return font_info->font_idx;
+}
+
+
+/* Return RFONT-DEF (vector) in the realized fontset FONTSET for the
+ character C. If the corresponding font is not yet opened, open it
+ (if FACE is not NULL) or return Qnil (if FACE is NULL).
+ If no proper font is found for C, return Qnil. */
+
+static Lisp_Object
+fontset_font (fontset, c, face, id)
+ Lisp_Object fontset;
+ int c;
+ struct face *face;
+ int id;
+{
+ Lisp_Object base_fontset, elt, vec;
+ int i, from, to;
+ int font_idx;
+ FRAME_PTR f = XFRAME (FONTSET_FRAME (fontset));
+
+ base_fontset = FONTSET_BASE (fontset);
+ vec = CHAR_TABLE_REF (fontset, c);
+ if (EQ (vec, Qt))
+ goto try_fallback;
+
+ if (NILP (vec))
+ {
+ /* We have not yet decided a face for C. */
+ Lisp_Object range;
+
+ if (! face)
+ return Qnil;
+ elt = FONTSET_REF_AND_RANGE (base_fontset, c, from, to);
+ range = Fcons (make_number (from), make_number (to));
+ if (NILP (elt))
+ {
+ /* Record that we have no font for characters of this
+ range. */
+ vec = Qt;
+ FONTSET_SET (fontset, range, vec);
+ goto try_fallback;
+ }
+ /* Build a vector [ -1 -1 nil NEW-ELT0 NEW-ELT1 NEW-ELT2 ... ],
+ where the first -1 is to force reordering of NEW-ELTn,
+ NEW-ETLn is [nil nil AREF (elt, n) nil]. */
+ vec = Fmake_vector (make_number (ASIZE (elt) + 3), make_number (-1));
+ ASET (vec, 2, Qnil);
+ for (i = 0; i < ASIZE (elt); i++)
+ {
+ Lisp_Object tmp;
+
+ tmp = Fmake_vector (make_number (4), Qnil);
+ ASET (tmp, 2, AREF (elt, i));
+ ASET (vec, 3 + i, tmp);
+ }
+ /* Then store it in the fontset. */
+ FONTSET_SET (fontset, range, vec);
+ }
+
+ retry:
+ if (XINT (AREF (vec, 0)) != charset_ordered_list_tick)
+ /* The priority of charsets is changed after we selected a face
+ for C last time. */
+ reorder_font_vector (vec);
+
+ if (id < 0)
+ i = 3;
+ else if (id == XFASTINT (AREF (vec, 1)))
+ i = 2;
+ else
+ {
+ ASET (vec, 1, make_number (id));
+ for (i = 3; i < ASIZE (vec); i++)
+ if (id == XFASTINT (AREF (AREF (AREF (vec, i), 2), 1)))
+ break;
+ if (i < ASIZE (vec))
+ {
+ ASET (vec, 2, AREF (vec, i));
+ i = 2;
+ }
+ else
+ {
+ ASET (vec, 2, Qnil);
+ i = 3;
+ }
+ }
+
+ /* Find the first available font in the vector of RFONT-DEF. */
+ for (; i < ASIZE (vec); i++)
+ {
+ Lisp_Object font_def;
+
+ elt = AREF (vec, i);
+ if (NILP (elt))
+ continue;
+ /* ELT == [ FACE-ID FONT-INDEX FONT-DEF OPENED-FONT-NAME ] */
+ if (INTEGERP (AREF (elt, 1)) && XINT (AREF (elt, 1)) < 0)
+ /* We couldn't open this font last time. */
+ continue;
+
+ if (!face && NILP (AREF (elt, 1)))
+ /* We have not yet opened the font. */
+ return Qnil;
+
+ font_def = AREF (elt, 2);
+ /* FONT_DEF == [ FONT-SPEC ENCODING REPERTORY ] */
+ if (INTEGERP (AREF (font_def, 2)))
+ {
+ /* The repertory is specified by charset ID. */
+ struct charset *charset
+ = CHARSET_FROM_ID (XINT (AREF (font_def, 2)));
+
+ if (! CHAR_CHARSET_P (c, charset))
+ /* This font can't display C. */
+ continue;
+ }
+ else if (CHAR_TABLE_P (AREF (font_def, 2)))
+ {
+ /* The repertory is specified by a char table. */
+ if (NILP (CHAR_TABLE_REF (AREF (font_def, 2), c)))
+ /* This font can't display C. */
+ continue;
+ }
+ else
+ {
+ Lisp_Object slot;
+
+ if (! INTEGERP (AREF (elt, 1)))
+ {
+ /* We have not yet opened a font matching this spec.
+ Open the best matching font now and register the
+ repertory. */
+ struct font_info *font_info;
+
+ font_idx = load_font_get_repertory (f, face, font_def, fontset);
+ ASET (elt, 1, make_number (font_idx));
+ if (font_idx < 0)
+ /* This means that we couldn't find a font matching
+ FONT_DEF. */
+ continue;
+ font_info = (*get_font_info_func) (f, font_idx);
+ ASET (elt, 3, build_string (font_info->full_name));
+ }
+
+ slot = Fassq (AREF (elt, 1), FONTSET_REPERTORY (fontset));
+ xassert (CONSP (slot));
+ if (NILP (CHAR_TABLE_REF (XCDR (slot), c)))
+ /* This font can't display C. */
+ continue;
+ }
+
+ /* Now we have decided to use this font spec to display C. */
+ if (! INTEGERP (AREF (elt, 1)))
+ {
+ /* But not yet opened the best matching font. */
+ struct font_info *font_info;
+
+ font_idx = load_font_get_repertory (f, face, font_def, fontset);
+ ASET (elt, 1, make_number (font_idx));
+ if (font_idx < 0)
+ /* Can't open it. Try the other one. */
+ continue;
+ font_info = (*get_font_info_func) (f, font_idx);
+ ASET (elt, 3, build_string (font_info->full_name));
+ }
+
+ /* Now we have the opened font. */
+ return elt;
+ }
+
+ try_fallback:
+ if (! EQ (vec, FONTSET_FALLBACK (fontset)))
+ {
+ vec = FONTSET_FALLBACK (fontset);
+ if (VECTORP (vec))
+ goto retry;
+ if (EQ (vec, Qt))
+ goto try_default;
+ elt = FONTSET_FALLBACK (base_fontset);
+ if (! NILP (elt))
+ {
+ vec = Fmake_vector (make_number (ASIZE (elt) + 3), make_number (-1));
+ ASET (vec, 2, Qnil);
+ for (i = 0; i < ASIZE (elt); i++)
+ {
+ Lisp_Object tmp;
+
+ tmp = Fmake_vector (make_number (4), Qnil);
+ ASET (tmp, 2, AREF (elt, i));
+ ASET (vec, 3 + i, tmp);
+ }
+ FONTSET_FALLBACK (fontset) = vec;
+ goto retry;
+ }
+ /* Record that this fontset has no fallback fonts. */
+ FONTSET_FALLBACK (fontset) = Qt;
+ }
+
+ /* Try the default fontset. */
+ try_default:
+ if (! EQ (base_fontset, Vdefault_fontset))
+ {
+ if (NILP (FONTSET_DEFAULT (fontset)))
+ FONTSET_DEFAULT (fontset)
+ = make_fontset (FONTSET_FRAME (fontset), Qnil, Vdefault_fontset);
+ return fontset_font (FONTSET_DEFAULT (fontset), c, face, id);
+ }
+ return Qnil;
+}
+
+
+/* Return a newly created fontset with NAME. If BASE is nil, make a
+ base fontset. Otherwise make a realized fontset whose base is
+ BASE. */
+
+static Lisp_Object
+make_fontset (frame, name, base)
+ Lisp_Object frame, name, base;
+{
+ Lisp_Object fontset;
+ int size = ASIZE (Vfontset_table);
+ int id = next_fontset_id;
+
+ /* Find a free slot in Vfontset_table. Usually, next_fontset_id is
+ the next available fontset ID. So it is expected that this loop
+ terminates quickly. In addition, as the last element of
+ Vfontset_table is always nil, we don't have to check the range of
+ id. */
+ while (!NILP (AREF (Vfontset_table, id))) id++;
+
+ if (id + 1 == size)
+ {
+ /* We must grow Vfontset_table. */
+ Lisp_Object tem;