/* fontset.c -- fontset module.
Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
National Institute of Advanced Industrial Science and Technology (AIST)
Registration Number H15PRO112
This file is part of the m17n library.
The m17n library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
The m17n library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the m17n library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA. */
/***en
@addtogroup m17nFontset
@brief A fontset is an object that maps a character to fonts.
A @e fontset is an object of the type @c MFontset. When drawing an
M-text, a fontset provides rules to select a font for each
character in the M-text according to the following information.
@li The script character property of a character.
@li The language text property of a character.
@li The charset text property of a character.
The documentation of mdraw_text () describes how that information is
used. */
/***ja @addtogroup m17nFontset
@brief フォントセットは文字からフォントへの対応付けを行うオブジェクトである.
@e フォントセット は @c MFontset 型のオブジェクトである。M-text
の表示の際、フォントセットは以下の情報を用いて M-text
中の個々の文字にどのフォントを用いるか決める規則を与える。
@li 文字の文字プロパティ "スクリプト"
@li 文字のテキストプロパティ "言語"
@li 文字のテキストプロパティ "文字セット"
これらの情報がどのように用いられるかは mdraw_text () の説明を参照のこと。
*/
/*=*/
#if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
/*** @addtogroup m17nInternal
@{ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "config.h"
#include "m17n-gui.h"
#include "m17n-misc.h"
#include "internal.h"
#include "symbol.h"
#include "plist.h"
#include "character.h"
#include "charset.h"
#include "internal-gui.h"
#include "font.h"
#include "fontset.h"
static int mdebug_flag = MDEBUG_FONTSET;
static M17NObjectArray fontset_table;
struct MFontset
{
M17NObject control;
/* Name of the fontset. */
MSymbol name;
/* Initialized to 0, and incremented by one each time the fontset is
modified. */
unsigned tick;
/* Database from which to load the contents of the fontset. Once
loaded, this member is set to NULL. */
MDatabase *mdb;
/* SCRIPT vs PER-LANGUAGE (which is a plist LANGUAGE vs FONT-GROUP) */
MPlist *per_script;
/* CHARSET vs FONT-GROUP */
MPlist *per_charset;
/* FONT-GROUP */
MPlist *fallback;
};
static MFontset *default_fontset;
static MPlist *fontset_list;
struct MRealizedFontset
{
/* Fontset from which the realized fontset is realized. */
MFontset *fontset;
/* Initialized to <fontset>->tick. */
unsigned tick;
/* Font spec that must be satisfied, or NULL. */
MFont *spec;
/* Font spec requested by a face. */
MFont request;
/* The frame on which the realized fontset is realized. */
MFrame *frame;
MPlist *per_script;
MPlist *per_charset;
MPlist *fallback;
};
static MPlist *
load_font_group (MPlist *plist, MPlist *elt)
{
MPLIST_DO (elt, elt)
{
/* ELT ::= ( FONT-SPEC [ LAYOUTER ] ) ... */
MPlist *elt2;
MFont *font;
MSymbol layouter_name;
if (! MPLIST_PLIST_P (elt))
MWARNING (MERROR_FONTSET);
elt2 = MPLIST_PLIST (elt);
if (! MPLIST_PLIST_P (elt2))
MWARNING (MERROR_FONTSET);
MSTRUCT_CALLOC (font, MERROR_FONTSET);
mfont__set_spec_from_plist (font, MPLIST_PLIST (elt2));
elt2 = MPLIST_NEXT (elt2);
layouter_name = Mt;
if (MPLIST_SYMBOL_P (elt2))
layouter_name = MPLIST_SYMBOL (elt2);
if (layouter_name == Mnil)
layouter_name = Mt;
plist = mplist_add (plist, layouter_name, font);
continue;
warning:
/* ANSI-C requires some statement after a label. */
continue;
}
return plist;
}
/* Load FONTSET->per_script from the data in FONTSET->mdb. */
static void
load_fontset_contents (MFontset *fontset)
{
MPlist *per_script, *per_charset, *font_group;
MPlist *fontset_def, *plist;
fontset->per_script = per_script = mplist ();
fontset->per_charset = per_charset = mplist ();
fontset->fallback = mplist ();
if (! (fontset_def = (MPlist *) mdatabase_load (fontset->mdb)))
return;
MPLIST_DO (plist, fontset_def)
{
/* PLIST ::= ( SCRIPT ( LANGUAGE ( FONT-SPEC [LAYOUTER]) ... ) ... )
| ( CHARSET ( FONT-SPEC [LAYOUTER] ) ...)
| ( nil ( FONT-SPEC [LAYOUTER] ) ...)
FONT-SPEC :: = ( ... ) */
MPlist *elt;
MSymbol sym;
if (! MPLIST_PLIST_P (plist))
MWARNING (MERROR_FONTSET);
elt = MPLIST_PLIST (plist);
if (! MPLIST_SYMBOL_P (elt))
MWARNING (MERROR_FONTSET);
sym = MPLIST_SYMBOL (elt);
elt = MPLIST_NEXT (elt);
if (! MPLIST_PLIST_P (elt))
MWARNING (MERROR_FONTSET);
if (sym == Mnil)
load_font_group (fontset->fallback, elt);
else if (MPLIST_PLIST_P (MPLIST_PLIST (elt)))
{
/* SYM is a charset. */
font_group = mplist ();
per_charset = mplist_add (per_charset, sym, font_group);
load_font_group (font_group, elt);
}
else
{
/* SYM is a script */
MPlist *per_lang = mplist ();
per_script = mplist_add (per_script, sym, per_lang);
MPLIST_DO (elt, elt)
{
/* ELT ::= ( LANGUAGE FONT-DEF ...) ... */
MPlist *elt2;
MSymbol lang;
if (! MPLIST_PLIST_P (elt))
MWARNING (MERROR_FONTSET);
elt2 = MPLIST_PLIST (elt);
if (! MPLIST_SYMBOL_P (elt2))
MWARNING (MERROR_FONTSET);
lang = MPLIST_SYMBOL (elt2);
if (lang == Mnil)
lang = Mt;
font_group = mplist ();
mplist_add (per_lang, lang, font_group);
elt2 = MPLIST_NEXT (elt2);
load_font_group (font_group, elt2);
}
}
continue;
warning:
/* ANSI-C requires some statement after a label. */
continue;
}
M17N_OBJECT_UNREF (fontset_def);
fontset->mdb = NULL;
}
static void
free_fontset (void *object)
{
MFontset *fontset = (MFontset *) object;
MPlist *plist, *pl, *p;
if (fontset->per_script)
{
MPLIST_DO (plist, fontset->per_script)
{
MPLIST_DO (pl, MPLIST_PLIST (plist))
{
MPLIST_DO (p, MPLIST_PLIST (pl))
free (MPLIST_VAL (p));
p = MPLIST_PLIST (pl);
M17N_OBJECT_UNREF (p);
}
pl = MPLIST_PLIST (plist);
M17N_OBJECT_UNREF (pl);
}
M17N_OBJECT_UNREF (fontset->per_script);
}
if (fontset->per_charset)
{
MPLIST_DO (pl, fontset->per_charset)
{
MPLIST_DO (p, MPLIST_PLIST (pl))
free (MPLIST_VAL (p));
p = MPLIST_PLIST (p);
M17N_OBJECT_UNREF (p);
}
M17N_OBJECT_UNREF (fontset->per_charset);
}
if (fontset->fallback)
{
MPLIST_DO (p, fontset->fallback)
free (MPLIST_VAL (p));
M17N_OBJECT_UNREF (fontset->fallback);
}
plist = mplist_find_by_key (fontset_list, fontset->name);
if (! plist)
mdebug_hook ();
mplist_pop (plist);
if (MPLIST_TAIL_P (fontset_list))
{
M17N_OBJECT_UNREF (fontset_list);
fontset_list = NULL;
}
M17N_OBJECT_UNREGISTER (fontset_table, fontset);
free (object);
}
static void
realize_fontset_elements (MFrame *frame, MRealizedFontset *realized)
{
MFontset *fontset = realized->fontset;
MPlist *per_script, *per_charset, *font_group;
MPlist *plist, *p;
realized->per_script = per_script = mplist ();
/* The actual elements of per_script are realized on demand. */
#if 0
MPLIST_DO (plist, fontset->per_script)
{
MPlist *pl;
per_lang = mplist ();
per_script = mplist_add (per_script, MPLIST_KEY (plist), per_lang);
MPLIST_DO (pl, MPLIST_PLIST (plist))
{
font_group = mplist ();
per_lang = mplist_add (per_lang, MPLIST_KEY (pl), font_group);
MPLIST_DO (p, MPLIST_PLIST (pl))
font_group = mplist_add (font_group,
MPLIST_KEY (p), MPLIST_VAL (p));
}
}
#endif
realized->per_charset = per_charset = mplist ();
MPLIST_DO (plist, fontset->per_charset)
{
font_group = mplist ();
per_charset = mplist_add (per_charset, MPLIST_KEY (plist), font_group);
MPLIST_DO (p, MPLIST_PLIST (plist))
font_group = mplist_add (font_group, MPLIST_KEY (p), MPLIST_VAL (p));
}
realized->fallback = font_group = mplist ();
MPLIST_DO (p, fontset->fallback)
font_group = mplist_add (font_group, MPLIST_KEY (p), MPLIST_VAL (p));
}
/* Return a plist of fonts for SCRIPT in FONTSET. The returned list
is acutally a plist of languages vs font groups (which is a plist).
If SCRIPT is nil, return a plist of fallback fonts. If FONTSET
doesn't record any fonts for SCRIPT, generate a proper font spec
lists for X backend and FreeType backend. */
MPlist *
get_per_script (MFontset *fontset, MSymbol script)
{
MPlist *plist;
if (script == Mnil)
return fontset->fallback;
plist = mplist_get (fontset->per_script, script);
if (! plist)
{
int len = MSYMBOL_NAMELEN (script);
char *cap = alloca (8 + len + 1);
MSymbol capability;
MFont *font;
MPlist *pl, *p;
sprintf (cap, ":script=%s", MSYMBOL_NAME (script));
capability = msymbol (cap);
pl = mplist ();
MPLIST_DO (p, fontset->fallback)
{
font = mfont_copy (MPLIST_VAL (p));
mfont_put_prop (font, Mregistry, Municode_bmp);
font->source = MFONT_SOURCE_FT;
font->capability = capability;
mplist_add (pl, Mt, font);
font = mfont_copy (MPLIST_VAL (p));
mfont_put_prop (font, Mregistry, Miso10646_1);
font->source = MFONT_SOURCE_X;
font->capability = capability;
mplist_add (pl, Mt, font);
}
plist = mplist ();
mplist_add (plist, Mt, pl);
mplist_add (fontset->per_script, script, plist);
}
return plist;
}
static void
free_realized_fontset_elements (MRealizedFontset *realized)
{
MPlist *plist, *pl, *p;
MFont *font;
MFontList *font_list;
if (realized->per_script)
{
MPLIST_DO (plist, realized->per_script)
{
MPLIST_DO (pl, MPLIST_PLIST (plist))
{
MPLIST_DO (p, MPLIST_PLIST (pl))
{
font = MPLIST_VAL (p);
if (font->type == MFONT_TYPE_OBJECT)
{
font_list = (MFontList *) font;
free (font_list->fonts);
free (font_list);
}
/* This is to avoid freeing rfont again by the later
M17N_OBJECT_UNREF (p) */
MPLIST_KEY (p) = Mt;
}
p = MPLIST_PLIST (pl);
M17N_OBJECT_UNREF (p);
}
pl = MPLIST_PLIST (plist);
M17N_OBJECT_UNREF (pl);
}
M17N_OBJECT_UNREF (realized->per_script);
}
if (realized->per_charset)
{
MPLIST_DO (plist, realized->per_charset)
{
MPLIST_DO (pl, MPLIST_PLIST (plist))
{
font = MPLIST_VAL (pl);
if (font->type == MFONT_TYPE_OBJECT)
{
font_list = (MFontList *) font;
free (font_list->fonts);
free (font_list);
}
MPLIST_KEY (pl) = Mt;
}
pl = MPLIST_PLIST (plist);
M17N_OBJECT_UNREF (pl);
}
M17N_OBJECT_UNREF (realized->per_charset);
}
if (realized->fallback)
{
MPLIST_DO (plist, realized->fallback)
{
font = MPLIST_VAL (plist);
if (font->type == MFONT_TYPE_OBJECT)
{
font_list = (MFontList *) font;
free (font_list->fonts);
free (font_list);
}
MPLIST_KEY (plist) = Mt;
}
M17N_OBJECT_UNREF (realized->fallback);
}
}
static void
update_fontset_elements (MRealizedFontset *realized)
{
free_realized_fontset_elements (realized);
realize_fontset_elements (realized->frame, realized);
}
/* Internal API */
int
mfont__fontset_init ()
{
M17N_OBJECT_ADD_ARRAY (fontset_table, "Fontset");
Mfontset = msymbol ("fontset");
Mfontset->managing_key = 1;
fontset_list = mplist ();
default_fontset = mfontset ("default");
if (! default_fontset->mdb)
{
MFont font;
MFONT_INIT (&font);
mfont_put_prop (&font, Mregistry, msymbol ("iso8859-1"));
mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil,
&font, Mnil, 1);
mfont_put_prop (&font, Mregistry, msymbol ("iso10646-1"));
mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil,
&font, Mnil, 1);
}
return 0;
}
void
mfont__fontset_fini ()
{
M17N_OBJECT_UNREF (default_fontset);
default_fontset = NULL;
}
MRealizedFontset *
mfont__realize_fontset (MFrame *frame, MFontset *fontset,
MFace *face, MFont *spec)
{
MRealizedFontset *realized;
MFont request;
MPlist *plist;
if (fontset->mdb)
load_fontset_contents (fontset);
MFONT_INIT (&request);
mfont__set_spec_from_face (&request, face);
if (request.size <= 0)
{
mdebug_hook ();
request.size = 120;
}
MPLIST_DO (plist, frame->realized_fontset_list)
{
realized = (MRealizedFontset *) MPLIST_VAL (plist);
if (fontset->name == MPLIST_KEY (plist)
&& ! memcmp (&request, &realized->request, sizeof (MFont))
&& (realized->spec
? (spec && ! memcmp (spec, &realized->spec, sizeof (MFont)))
: ! spec))
return realized;
}
MSTRUCT_CALLOC (realized, MERROR_FONTSET);
realized->fontset = fontset;
M17N_OBJECT_REF (fontset);
realized->tick = fontset->tick;
if (spec)
{
MSTRUCT_CALLOC (realized->spec, MERROR_FONTSET);
*realized->spec = *spec;
}
realized->request = request;
realized->frame = frame;
realize_fontset_elements (frame, realized);
mplist_add (frame->realized_fontset_list, fontset->name, realized);
return realized;
}
void
mfont__free_realized_fontset (MRealizedFontset *realized)
{
free_realized_fontset_elements (realized);
M17N_OBJECT_UNREF (realized->fontset);
if (realized->spec)
free (realized->spec);
free (realized);
}
static MRealizedFont *
try_font_list (MFrame *frame, MFontList *font_list, MFont *request,
MSymbol layouter, MGlyph *g, int *num, int all, int exact)
{
int i, j;
MFont *font;
MRealizedFont *rfont;
for (i = 0; i < font_list->nfonts; i++)
{
if (font_list->fonts[i].font->type == MFONT_TYPE_SPEC)
MFATAL (MERROR_FONT);
if (exact)
{
if (font_list->fonts[i].score > 0)
break;
}
else
{
if (font_list->fonts[i].score == 0)
continue;
}
font = font_list->fonts[i].font;
if (font->type == MFONT_TYPE_FAILURE)
continue;
/* Check if this font can display all glyphs. */
for (j = 0; j < *num; j++)
{
int c = g[j].type == GLYPH_CHAR ? g[j].g.c : ' ';
MFLT *flt;
MCharTable *coverage;
if (layouter != Mt
? ((flt = mflt_get (layouter))
? (coverage = mflt_coverage (flt),
! mchartable_lookup (coverage, c))
: 0)
: ! mfont__has_char (frame, font, &font_list->object, c))
break;
}
if (j == 0 && *num > 0)
continue;
if (j == *num || !all)
{
MCharTable *coverage = NULL;
/* We found a font that can display the requested range of
glyphs. */
if (font->type == MFONT_TYPE_REALIZED)
rfont = (MRealizedFont *) font;
else
{
rfont = mfont__open (frame, font, &font_list->object);
if (! rfont)
continue;
font_list->fonts[i].font = (MFont *) rfont;
}
rfont->layouter = layouter == Mt ? Mnil : layouter;
if (rfont->layouter)
{
MFLT *flt = mflt_get (rfont->layouter);
if (flt)
coverage = mflt_coverage (flt);
}
*num = j;
for (j = 0; j < *num; j++)
{
int c = g[j].type == GLYPH_CHAR ? g[j].g.c : ' ';
g[j].g.code = (coverage
? (unsigned ) mchartable_lookup (coverage, c)
: mfont__encode_char (frame, (MFont *) rfont,
&font_list->object, c));
}
return rfont;
}
}
return NULL;
}
static MRealizedFont *
try_font_group (MRealizedFontset *realized, MFont *request,
MPlist *font_group, MGlyph *g, int *num, int size)
{
MFrame *frame = realized->frame;
MFont *font;
MFontList *font_list;
MRealizedFont *rfont;
MPlist *plist;
MSymbol layouter;
int best_score = -1, worst_score;
for (plist = font_group; ! MPLIST_TAIL_P (plist); )
{
int this_score;
layouter = MPLIST_KEY (plist);
font = MPLIST_VAL (plist);
if (font->type == MFONT_TYPE_SPEC)
{
/* We have not yet made this entry a MFontList. */
if (realized->spec)
{
MFont this = *font;
if (mfont__merge (&this, realized->spec, 1) < 0)
{
mplist_pop (plist);
continue;
}
font_list = mfont__list (frame, &this, &this, size);
}
else
font_list = mfont__list (frame, font, request, size);
if (! font_list)
{
/* As there's no font matching this spec, remove this
element from the font group. */
mplist_pop (plist);
continue;
}
MPLIST_VAL (plist) = font_list;
}
else
font_list = (MFontList *) font;
this_score = font_list->fonts[0].score;
if ((this_score == 0)
&& (rfont = try_font_list (frame, font_list, request,
layouter, g, num, 1, 1)))
return rfont;
if (best_score < 0)
{
best_score = worst_score = this_score;
plist = MPLIST_NEXT (plist);
}
else if (this_score >= worst_score)
{
worst_score = this_score;
plist = MPLIST_NEXT (plist);
}
else
{
MPlist *pl;
MPLIST_DO (pl, font_group)
if (this_score < ((MFontList *) MPLIST_VAL (pl))->fonts[0].score)
break;
mplist_pop (plist);
mplist_push (pl, layouter, font_list);
}
}
/* We couldn't find an exact matching font that can display all
glyphs. Find one that can at least display all glyphs. */
MPLIST_DO (plist, font_group)
{
rfont = try_font_list (frame, MPLIST_VAL (plist), request,
MPLIST_KEY (plist), g, num, 1, 0);
if (rfont)
return rfont;
}
/* We couldn't find a font that can display all glyphs. Find an
exact matching font that can at least display the first
glyph. */
MPLIST_DO (plist, font_group)
{
rfont = try_font_list (frame, MPLIST_VAL (plist), request,
MPLIST_KEY (plist), g, num, 0, 1);
if (rfont)
return rfont;
}
/* Find any font that can at least display the first glyph. */
MPLIST_DO (plist, font_group)
{
rfont = try_font_list (frame, MPLIST_VAL (plist), request,
MPLIST_KEY (plist), g, num, 0, 0);
if (rfont)
return rfont;
}
return NULL;
}
MRealizedFont *
mfont__lookup_fontset (MRealizedFontset *realized, MGlyph *g, int *num,
MSymbol script, MSymbol language, MSymbol charset,
int size, int ignore_fallback)
{
MCharset *preferred_charset = (charset == Mnil ? NULL : MCHARSET (charset));
MPlist *per_charset, *per_script, *per_lang;
MPlist *plist;
MRealizedFont *rfont = NULL;
if (MDEBUG_FLAG ())
{
int i;
MDEBUG_PRINT1 (" [FONTSET] fontset looking up for %s:",
script ? script->name : "none");
for (i = 0; i < *num; i++)
MDEBUG_PRINT1 (" U+%04X", g[i].g.c);
MDEBUG_PRINT ("\n");
}
if (realized->tick != realized->fontset->tick)
update_fontset_elements (realized);
if (preferred_charset
&& (per_charset = mplist_get (realized->per_charset, charset)) != NULL
&& (rfont = try_font_group (realized, &realized->request, per_charset,
g, num, size)))
goto done;
if (script != Mnil)
{
MFont request = realized->request;
if (script != Mlatin)
/* This is not appropriate for non-Latin scripts. */
request.property[MFONT_REGISTRY] = 0;
per_script = mplist_get (realized->per_script, script);
if (! per_script)
{
per_script = mplist_copy (get_per_script (realized->fontset, script));
/* PER_SCRIPT ::= (LANGUAGE:(LAYOUTER:FONT-SPEC ...) ...) */
MPLIST_DO (plist, per_script)
MPLIST_VAL (plist) = mplist_copy (MPLIST_VAL (plist));
mplist_add (realized->per_script, script, per_script);
}
/* We prefer font groups in this order:
(1) group matching with LANGUAGE if LANGUAGE is not Mnil
(2) group for generic language
(3) group not matching with LANGUAGE */
if (language == Mnil)
language = Mt;
if ((per_lang = mplist_get (per_script, language))
&& (rfont = try_font_group (realized, &request, per_lang,
g, num, size)))
goto done;
if (per_lang && *num > 1)
*num = 1;
if (language == Mt)
{
/* Try the above (3) */
MPLIST_DO (plist, per_script)
if (MPLIST_KEY (plist) != language
&& (rfont = try_font_group (realized, &request,
MPLIST_PLIST (plist),
g, num, size)))
goto done;
}
else
{
/* At first try the above (2) */
if ((per_lang = mplist_get (per_script, Mt))
&& (rfont = try_font_group (realized, &request, per_lang,
g, num, size)))
goto done;
if (per_lang && *num > 1)
*num = 1;
/* Then try the above (3) */
MPLIST_DO (plist, per_script)
if (MPLIST_KEY (plist) != language
&& MPLIST_KEY (plist) != Mt
&& (rfont = try_font_group (realized, &request,
MPLIST_PLIST (plist),
g, num, size)))
goto done;
}
if (ignore_fallback)
goto done;
}
if (language != Mnil)
/* Find a font group for this language from all scripts. */
MPLIST_DO (plist, realized->per_script)
{
MFont request = realized->request;
if (MPLIST_KEY (plist) != Mlatin)
request.property[MFONT_FOUNDRY]
= request.property[MFONT_FAMILY]
= request.property[MFONT_FAMILY] = 0;
if ((per_lang = mplist_get (MPLIST_PLIST (plist), language))
&& (rfont = try_font_group (realized, &request, per_lang,
g, num, size)))
goto done;
}
/* Try fallback fonts. */
rfont = try_font_group (realized, &realized->request,
realized->fallback, g, num, size);
done:
if (MDEBUG_FLAG ())
{
if (rfont)
{
MSymbol family = mfont_get_prop (rfont->font, Mfamily);
MDEBUG_PRINT1 (" [FONTSET] found %s\n", family->name);
}
else
MDEBUG_PRINT (" [FONTSET] not found\n");
}
return rfont;
}
MRealizedFont *
get_font_from_group (MFrame *frame, MPlist *plist, MFont *font)
{
MRealizedFont *rfont;
MPLIST_DO (plist, plist)
{
MFont spec = *(MFont *) MPLIST_VAL (plist);
if (mfont__merge (&spec, font, 1) < 0)
continue;
if (font->type == MFONT_TYPE_SPEC)
rfont = (MRealizedFont *) mfont_find (frame, &spec, NULL, 0);
else if (font->type == MFONT_TYPE_OBJECT)
rfont = mfont__open (frame, font, &spec);
else
rfont = (MRealizedFont *) font;
if (rfont
&& (spec.capability == Mnil
|| mfont__check_capability (rfont, spec.capability) == 0))
{
rfont->layouter
= MPLIST_KEY (plist) == Mt ? Mnil : MPLIST_KEY (plist);
return rfont;
}
}
return NULL;
}
MRealizedFont *
mfontset__get_font (MFrame *frame, MFontset *fontset,
MSymbol script, MSymbol language, MFont *font,
int *best)
{
MPlist *per_script, *per_lang;
MRealizedFont *rfont;
if (best)
*best = 0;
if (language == Mnil)
language = Mt;
if (script != Mnil)
{
per_script = get_per_script (fontset, script);
if ((per_lang = mplist_get (per_script, language))
&& (rfont = get_font_from_group (frame, per_lang, font)))
{
if (best)
*best = 1;
return rfont;
}
if (best)
*best = per_lang ? 0 : 1;
if (language == Mt)
{
MPLIST_DO (per_script, per_script)
if (MPLIST_KEY (per_script) != language
&& (rfont = get_font_from_group (frame,
MPLIST_PLIST (per_script),
font)))
return rfont;
}
else
{
if ((per_lang = mplist_get (per_script, Mt))
&& (rfont = get_font_from_group (frame, per_lang, font)))
return rfont;
if (best)
*best = 0;
MPLIST_DO (per_script, per_script)
if (MPLIST_KEY (per_script) != language
&& MPLIST_KEY (per_script) != Mt
&& (rfont = get_font_from_group (frame,
MPLIST_PLIST (per_script),
font)))
return rfont;
}
}
if (language != Mt)
MPLIST_DO (per_script, fontset->per_script)
{
if ((per_lang = mplist_get (MPLIST_PLIST (per_script), language))
&& (rfont = get_font_from_group (frame, per_lang, font)))
{
if (best)
*best = 1;
return rfont;
}
}
if (best)
*best = 0;
if ((rfont = get_font_from_group (frame, fontset->fallback, font)))
return rfont;
return NULL;
}
/*** @} */
#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
/* External API */
/*** @addtogroup m17nFontset */
/*** @{ */
/*=*/
/***en
@brief Return a fontset.
The mfontset () function returns a pointer to a fontset object of
name $NAME. If $NAME is @c NULL, it returns a pointer to the
default fontset.
If no fontset has the name $NAME, a new one is created. At that
time, if there exists a data \<@c fontset, $NAME\> in the m17n
database, the fontset contents are initialized according to the
data. If no such data exists, the fontset contents are left
vacant.
The macro M17N_INIT () creates the default fontset. An
application program can modify it before the first call of
mframe ().
@return
This function returns a pointer to the found or newly created
fontset. */
/***ja
@brief フォントセットを返す.
関数 mfontset () は名前 $NAME を持つフォントセットオブジェクトへのポインタを返す。
$NAME が @c NULL ならば、デフォルトフォントセットへのポインタを返す。
$NAME という名前を持つフォントセットがなければ、新しいものが作られる。その際、
m17n データベースに \<@c fontset, $NAME\>
というデータがあれば、フォントセットはそのデータに沿って初期化される。
なければ、空のままにされる。
マクロ M17N_INIT () はデフォルトのフォントセットを作る。アプリケーションプログラムは
mframe () を初めて呼ぶまでの間はデフォルトフォントセットを変更することができる。
@return
この関数は見つかった、あるいは作ったフォントセットへのポインタを返す。
*/
MFontset *
mfontset (char *name)
{
MSymbol sym;
MFontset *fontset;
if (! name)
{
fontset = default_fontset;
M17N_OBJECT_REF (fontset);
}
else
{
sym = msymbol (name);
fontset = mplist_get (fontset_list, sym);
if (fontset)
M17N_OBJECT_REF (fontset);
else
{
M17N_OBJECT (fontset, free_fontset, MERROR_FONTSET);
M17N_OBJECT_REGISTER (fontset_table, fontset);
fontset->name = sym;
fontset->mdb = mdatabase_find (Mfontset, sym, Mnil, Mnil);
if (! fontset->mdb)
{
fontset->per_script = mplist ();
fontset->per_charset = mplist ();
fontset->fallback = mplist ();
}
mplist_put (fontset_list, sym, fontset);
}
}
return fontset;
}
/*=*/
/***en
@brief Return the name of a fontset.
The mfontset_name () function returns the name of fontset $FONTSET. */
/***ja
@brief フォントセットの名前を返す.
関数 mfontset_name () はフォントセット $FONTSET の名前を返す。 */
MSymbol
mfontset_name (MFontset *fontset)
{
return fontset->name;
}
/*=*/
/***en
@brief Make a copy of a fontset.
The mfontset_copy () function makes a copy of fontset $FONTSET, gives it a
name $NAME, and returns a pointer to the created copy. $NAME must
not be a name of existing fontset. In such case, this function
returns NULL without making a copy. */
/***ja
@brief フォントセットのコピーを作る.
関数 mfontset_copy () はフォントセット $FONTSET のコピーを作って、名前
$NAME を与え、そのコピーへのポインタを返す。$NAME
は既存のフォントセットの名前であってはならない。そのような場合にはコピーを作らずに
NULL を返す。 */
MFontset *
mfontset_copy (MFontset *fontset, char *name)
{
MSymbol sym = msymbol (name);
MFontset *copy = mplist_get (fontset_list, sym);
MPlist *plist, *pl, *p;
if (copy)
return NULL;
M17N_OBJECT (copy, free_fontset, MERROR_FONTSET);
M17N_OBJECT_REGISTER (fontset_table, copy);
copy->name = sym;
if (fontset->mdb)
load_fontset_contents (fontset);
if (fontset->per_script)
{
copy->per_script = mplist ();
MPLIST_DO (plist, fontset->per_script)
{
MPlist *per_lang = mplist ();
mplist_add (copy->per_script, MPLIST_KEY (plist), per_lang);
MPLIST_DO (pl, MPLIST_PLIST (plist))
{
MPlist *font_group = mplist ();
per_lang = mplist_add (per_lang, MPLIST_KEY (pl), font_group);
MPLIST_DO (p, MPLIST_PLIST (pl))
font_group = mplist_add (font_group, MPLIST_KEY (p),
mfont_copy (MPLIST_VAL (p)));
}
}
}
if (fontset->per_charset)
{
MPlist *per_charset = mplist ();
copy->per_charset = per_charset;
MPLIST_DO (pl, fontset->per_charset)
{
MPlist *font_group = mplist ();
per_charset = mplist_add (per_charset, MPLIST_KEY (pl), font_group);
MPLIST_DO (p, MPLIST_PLIST (pl))
font_group = mplist_add (font_group, MPLIST_KEY (p),
mfont_copy (MPLIST_VAL (p)));
}
}
if (fontset->fallback)
{
MPlist *font_group = mplist ();
copy->fallback = font_group;
MPLIST_DO (p, fontset->fallback)
font_group = mplist_add (font_group, MPLIST_KEY (p),
mfont_copy (MPLIST_VAL (p)));
}
mplist_put (fontset_list, sym, copy);
return copy;
}
/*=*/
/***en
@brief Modify the contents of a fontset.
The mfontset_modify_entry () function associates, in fontset
$FONTSET, a copy of $FONT with the $SCRIPT / $LANGUAGE pair or
with $CHARSET.
Each font in a fontset is associated with a particular
script/language pair, with a particular charset, or with the
symbol @c Mnil. The fonts that are associated with the same item
make a group.
If $SCRIPT is not @c Mnil, it must be a symbol identifying a
script. In this case, $LANGUAGE is either a symbol identifying a
language or @c Mnil, and $FONT is associated with the $SCRIPT /
$LANGUAGE pair.
If $CHARSET is not @c Mnil, it must be a symbol representing a
charset object. In this case, $FONT is associated with that
charset.
If both $SCRIPT and $CHARSET are not @c Mnil, two copies of $FONT
are created. Then one is associated with the $SCRIPT / $LANGUAGE
pair and the other with that charset.
If both $SCRIPT and $CHARSET are @c Mnil, $FONT is associated with
@c Mnil. This kind of fonts are called @e fallback @e fonts.
The argument $HOW specifies the priority of $FONT. If $HOW is
positive, $FONT has the highest priority in the group of fonts
that are associated with the same item. If $HOW is negative,
$FONT has the lowest priority. If $HOW is zero, $FONT becomes the
only available font for the associated item; all the other fonts
are removed from the group.
If $LAYOUTER_NAME is not @c Mnil, it must be a symbol representing
a @ref mdbFLT (font layout table). In that case, if $FONT is
selected for drawing an M-text, that font layout table is used to
generate a glyph code sequence from a character sequence.
@return
If the operation was successful, mfontset_modify_entry () returns 0.
Otherwise it returns -1 and assigns an error code to the external
variable #merror_code. */
/***ja
@brief フォントセットの内容を変更する.
関数 mfontset_modify_entry () は、$LANGUAGE と $SCRIPT の組み合わせ、または
$CHARSET に対して $FONT のコピーを使うように、フォントセット $FONTSET を設定する。
フォントセット中の各フォントは、特定のスクリプトと言語のペア、特定の文字セット、シンボル
@c Mnil のいずれかと関連付けられている。同じものと関連付けられたフォントはグループを構成する。
$SCRIPT は @c Mnil であるか、スクリプトを特定するシンボルである。
シンボルである場合には、$LANGUAGE は言語を特定するシンボルか @c
Mnil であり、$FONT はthe $SCRIPT / $LANGUAGE ペアに関連付けられる。
$CHARSET は @c Mnil であるか、文字セットオブジェクトを表すシンボルである。
シンボルである場合には $FONT はその文字セットと関連付けられる。
$SCRIPT と $CHARSET の双方が @c Mnil でない場合には $FONT
のコピーが2つ作られ、それぞれ $SCRIPT / $LANGUAGE
ペアと文字セットに関連付けられる。
$SCRIPT と $CHARSET の双方が @c Mnil ならば、 $FONT は @c Mnil
と関連付けられる。この種のフォントは @e fallback @e font と呼ばれる。
引数 $HOW は $FONT の優先度を指定する。$HOW が正ならば、$FONT
は同じものと関連付けられたグループ中で最高の優先度を持つ。$HOW
が負ならば、最低の優先度を持つ。$HOW が 0 ならば、$FONT
は関連付けられたものに対する唯一の利用可能なフォントとなり、他のフォントはグループから取り除かれる。
$LAYOUTER_NAME は @c Mnil であるか、@ref mdbFLT
(フォントレイアウトテーブル)を示すシンボルである。シンボルであれば、$FONT を用いて
M-text を表示する際には、そのフォントレイアウトテーブルを使って文字列からグリフコード列を生成する。
@return
処理が成功したとき、mfontset_modify_entry () は 0 を返す。
失敗したときは -1 を返し、外部変数 #merror_code にエラーコードを設定する。 */
/***
@errors
@c MERROR_SYMBOL */
int
mfontset_modify_entry (MFontset *fontset,
MSymbol script, MSymbol language, MSymbol charset,
MFont *spec, MSymbol layouter_name,
int how)
{
MPlist *per_lang, *plist[3];
MFont *font = NULL;
int i;
if (fontset->mdb)
load_fontset_contents (fontset);
i = 0;
if (script != Mnil)
{
if (language == Mnil)
language = Mt;
per_lang = mplist_get (fontset->per_script, script);
if (! per_lang)
mplist_add (fontset->per_script, script, per_lang = mplist ());
plist[i] = mplist_get (per_lang, language);
if (! plist[i])
mplist_add (per_lang, language, plist[i] = mplist ());
i++;
}
if (charset != Mnil)
{
plist[i] = mplist_get (fontset->per_charset, charset);
if (! plist[i])
mplist_add (fontset->per_charset, charset, plist[i] = mplist ());
i++;
}
if (script == Mnil && charset == Mnil)
{
plist[i++] = fontset->fallback;
}
if (layouter_name == Mnil)
layouter_name = Mt;
for (i--; i >= 0; i--)
{
font = mfont_copy (spec);
font->type = MFONT_TYPE_SPEC;
if (how == 1)
mplist_push (plist[i], layouter_name, font);
else if (how == -1)
mplist_add (plist[i], layouter_name, font);
else
{
MPlist *pl;
MPLIST_DO (pl, plist[i])
free (MPLIST_VAL (pl));
mplist_set (plist[i], Mnil, NULL);
mplist_add (plist[i], layouter_name, font);
}
}
fontset->tick++;
return 0;
}
/*=*/
/***en
@brief Lookup a fontset.
The mfontset_lookup () function lookups $FONTSET and returns a
plist that describes the contents of $FONTSET corresponding to the
specified script, language, and charset.
If $SCRIPT is @c Mt, keys of the returned plist are script name
symbols for which some fonts are specified and values are NULL.
If $SCRIPT is a script name symbol, the returned plist is decided
by $LANGUAGE.
@li If $LANGUAGE is @c Mt, keys of the plist are language name
symbols for which some fonts are specified and values are NULL. A
key may be @c Mt which means some fallback fonts are specified for
the script.
@li If $LANGUAGE is a language name symbol, the plist is a @c
FONT-GROUP for the specified script and language. @c FONT-GROUP
is a plist whose keys are FLT (FontLayoutTable) name symbols (@c
Mt if no FLT is associated with the font) and values are pointers
to #MFont.
@li If $LANGUAGE is @c Mnil, the plist is fallback @c FONT-GROUP
for the script.
If $SCRIPT is @c Mnil, the returned plist is decided as below.
@li If $CHARSET is @c Mt, keys of the returned plist are charset name
symbols for which some fonts are specified and values are NULL.
@li If $CHARSET is a charset name symbol, the plist is a @c FONT-GROUP for
the charset.
@li If $CHARSET is @c Mnil, the plist is a fallback @c FONT-GROUP.
@return
It returns a plist describing the contents of a fontset. The
plist should be freed by m17n_object_unref (). */
/***ja
@brief フォントセットを検索する.
関数 mfontset_lookup () は $FONTSET を検索し、$FONTSET
の内容のうち指定したスクリプト、言語、文字セットに対応する部分を表す
plist を返す。
$SCRIPT が @c Mt ならば、返す plist
のキーはフォントが指定されているスクリプト名のシンボルであり、値は
NULL である。
$SCRIPT がスクリプト名のシンボルであれば、返す
plist は $LANGUAGEによって定まる。
@li $LANGUAGE が @c Mt ならば、plist
のキーはフォントが指定されている言語名のシンボルであり、値は
NULL である。キーは @c Mt
であることもあり、その場合そのスクリプトにフォールバックフォントがあることを意味する。
@li $LANGUAGE が言語名のシンボルならば、plist は指定のスクリプトと言語に対する
@c FONT-GROUP である。@c FONT-GROUP とは、キーが FLT
(FontLayoutTable) 名のシンボルであり、値が #MFont
へのポインタであるような plist である。ただしフォントに FLT
が対応付けられていない時には、キーは @c Mt になる。
@li $LANGUAGE が @c Mnil ならば、plist はそのスクリプト用のフォールバック
@c FONT-GROUP である。
$SCRIPT が @c Mnil ならば、返す plist は以下のように定まる。
@li $CHARSET が @c Mt ならば、plist
のキーはフォントが指定されている文字セット名のシンボルであり、値は
NULL である。
@li $CHARSET が文字セット名のシンボルならば、plist はその文字セット用の
@c FONT-GROUP である。
@li $CHARSET が @c Mnil ならば、plist はフォールバック @c FONT-GROUP である。
@return
この関数はフォントセットの内容を表す plist を返す。
plist は m17n_object_unref () で解放されるべきである。 */
MPlist *
mfontset_lookup (MFontset *fontset,
MSymbol script, MSymbol language, MSymbol charset)
{
MPlist *plist = mplist (), *pl, *p;
if (fontset->mdb)
load_fontset_contents (fontset);
if (script == Mt)
{
if (! fontset->per_script)
return plist;
p = plist;
MPLIST_DO (pl, fontset->per_script)
p = mplist_add (p, MPLIST_KEY (pl), NULL);
return plist;
}
if (script != Mnil)
{
pl = get_per_script (fontset, script);
if (MPLIST_TAIL_P (pl))
return plist;
if (language == Mt)
{
p = plist;
MPLIST_DO (pl, pl)
p = mplist_add (p, MPLIST_KEY (pl), NULL);
return plist;
}
if (language == Mnil)
language = Mt;
pl = mplist_get (pl, language);
}
else if (charset != Mnil)
{
if (! fontset->per_charset)
return plist;
if (charset == Mt)
{
p = plist;
MPLIST_DO (pl, fontset->per_charset)
p = mplist_add (p, MPLIST_KEY (pl), NULL);
return plist;
}
pl = mplist_get (fontset->per_charset, charset);
}
else
pl = fontset->fallback;
if (! pl)
return plist;
return mplist_copy (pl);
}
/*** @} */
/*** @addtogroup m17nDebug */
/*=*/
/*** @{ */
/***en
@brief Dump a fontset.
The mdebug_dump_fontset () function prints fontset $FONTSET in a
human readable way to the stderr or to what specified by the
environment variable MDEBUG_OUTPUT_FILE. $INDENT specifies how
many columns to indent the lines but the first one.
@return
This function returns $FONTSET. */
/***ja
@brief フォントセットをダンプする.
関数 mdebug_dump_face () はフォントセット $FONTSET を標準エラー出力
もしくは環境変数 MDEBUG_DUMP_FONT で指定されたファイルに人間に可読
な形で出力する。 $INDENT は2行目以降のインデントを指定する。
@return
この関数は $FONTSET を返す。 */
MFontset *
mdebug_dump_fontset (MFontset *fontset, int indent)
{
char *prefix = (char *) alloca (indent + 1);
MPlist *plist, *pl, *p;
memset (prefix, 32, indent);
prefix[indent] = 0;
fprintf (mdebug__output, "(fontset %s", fontset->name->name);
if (fontset->per_script)
MPLIST_DO (plist, fontset->per_script)
{
fprintf (mdebug__output, "\n %s(%s", prefix, MPLIST_KEY (plist)->name);
MPLIST_DO (pl, MPLIST_PLIST (plist))
{
fprintf (mdebug__output, "\n %s(%s", prefix,
MPLIST_KEY (pl)->name);
MPLIST_DO (p, MPLIST_PLIST (pl))
{
fprintf (mdebug__output, "\n %s(0x%X %s ", prefix,
(unsigned) MPLIST_VAL (p),
MPLIST_KEY (p)->name);
mdebug_dump_font (MPLIST_VAL (p));
fprintf (mdebug__output, ")");
}
fprintf (mdebug__output, ")");
}
fprintf (mdebug__output, ")");
}
if (fontset->per_charset)
MPLIST_DO (pl, fontset->per_charset)
{
fprintf (mdebug__output, "\n %s(%s", prefix, MPLIST_KEY (pl)->name);
MPLIST_DO (p, MPLIST_PLIST (pl))
{
fprintf (mdebug__output, "\n %s(%s ", prefix,
MPLIST_KEY (p)->name);
mdebug_dump_font (MPLIST_VAL (p));
fprintf (mdebug__output, ")");
}
fprintf (mdebug__output, ")");
}
if (fontset->fallback)
MPLIST_DO (p, fontset->fallback)
{
fprintf (mdebug__output, "\n %s(%s ", prefix, MPLIST_KEY (p)->name);
mdebug_dump_font (MPLIST_VAL (p));
fprintf (mdebug__output, ")");
}
fprintf (mdebug__output, ")");
return fontset;
}
/*** @} */
/*
Local Variables:
coding: euc-japan
End:
*/