Akira TAGOH avatar Akira TAGOH committed 1083569

Add the substitute font support

Comments (0)

Files changed (5)

libeasyfc/ezfc-config.c

 	ezfc_mem_t  parent;
 	GHashTable *aliases;
 	GHashTable *fonts;
+	GHashTable *subst;
 	gchar      *name;
 	gint        priority;
 	gboolean    loaded;
 	g_list_free(l);
 }
 
+static void
+_ezfc_config_subst_list_free(gpointer p)
+{
+	GList *l = p, *ll;
+
+	for (ll = l; ll != NULL; ll = g_list_next(ll)) {
+		ezfc_font_unref(ll->data);
+	}
+	g_list_free(l);
+}
+
 static ezfc_container_t *
 ezfc_container_new(ezfc_destroy_func_t func)
 {
 }
 
 static gboolean
+_ezfc_config_real_to_subst_xml(xmlNodePtr              root,
+			       const gchar            *family_name,
+			       const ezfc_container_t *container,
+			       gboolean               *no_elements)
+{
+	const GList *list, *l;
+	xmlNodePtr match, test, edit;
+
+	match = xmlNewNode(NULL,
+			   (const xmlChar *)"match");
+	xmlNewProp(match,
+		   (const xmlChar *)"target",
+		   (const xmlChar *)"pattern");
+	test = xmlNewChild(match, NULL,
+			   (const xmlChar *)"test",
+			   NULL);
+	xmlNewProp(test,
+		   (const xmlChar *)"name",
+		   (const xmlChar *)"family");
+	xmlNewChild(test, NULL,
+		    (const xmlChar *)"string",
+		    (const xmlChar *)family_name);
+	edit = xmlNewChild(match, NULL,
+			   (const xmlChar *)"edit",
+			   NULL);
+	xmlNewProp(edit,
+		   (const xmlChar *)"name",
+		   (const xmlChar *)"family");
+	xmlNewProp(edit,
+		   (const xmlChar *)"mode",
+		   (const xmlChar *)"append_last");
+	xmlNewProp(edit,
+		   (const xmlChar *)"binding",
+		   (const xmlChar *)"same");
+	list = ezfc_container_get(container);
+	for (l = list; l != NULL; l = g_list_next(l)) {
+		ezfc_font_t *f = l->data;
+		FcPattern *pat;
+		FcChar8 *n;
+		int i;
+
+		pat = ezfc_font_get_pattern(f);
+		for (i = 0; ; i++) {
+			if (FcPatternGetString(pat, FC_FAMILY, i, &n) != FcResultMatch)
+				break;
+			xmlNewChild(edit, NULL,
+				    (const xmlChar *)"string",
+				    (const xmlChar *)n);
+		}
+		FcPatternDestroy(pat);
+	}
+	xmlAddChild(root, match);
+	*no_elements = FALSE;
+
+	return TRUE;
+}
+
+static gboolean
 _ezfc_config_to_xml(ezfc_config_t  *config,
 		    xmlChar       **output,
 		    int            *size)
 		if (!_ezfc_config_real_to_font_xml(root, font, &no_elements))
 			return FALSE;
 	}
+	g_hash_table_iter_init(&iter, config->subst);
+	while (g_hash_table_iter_next(&iter, &key, &val)) {
+		const gchar *n = key;
+		const ezfc_container_t *container = val;
+
+		if (!_ezfc_config_real_to_subst_xml(root, n, container, &no_elements))
+			return FALSE;
+	}
 
 	xmlDocDumpFormatMemory(doc, output, size, 1);
 
 	return !no_elements;
 }
 
-static xmlChar *
+static GList *
 _ezfc_config_parse_string_node(xmlNodePtr node)
 {
-	xmlChar *retval = NULL;
+	xmlChar *s = NULL;
+	GList *retval = NULL;
 
 	while (node != NULL) {
-		if (node->type == XML_COMMENT_NODE || node->type == XML_TEXT_NODE)
-			node = node->next;
-		if (node->type == XML_ELEMENT_NODE &&
+		if (node->type == XML_COMMENT_NODE || node->type == XML_TEXT_NODE);
+		else if (node->type == XML_ELEMENT_NODE &&
 		    xmlStrcmp(node->name, (const xmlChar *)"string") == 0) {
-			retval = xmlNodeGetContent(node);
-			break;
+			s = xmlNodeGetContent(node);
+			retval = g_list_append(retval, s);
 		} else {
 			g_warning("Unexpected node: type = %d, name = %s",
 				  node->type, node->name);
 			break;
 		}
+		node = node->next;
 	}
 
 	return retval;
 	for (i = 0; i < n; i++) {
 		xmlNodePtr match = xmlXPathNodeSetItem(xobj->nodesetval, i);
 		xmlNodePtr node;
-		xmlChar *attr, *lang = NULL, *alias = NULL, *s;
+		xmlChar *attr, *lang = NULL, *alias = NULL;
 		FcPattern *pat = FcPatternCreate();
 		ezfc_alias_t *a;
+		ezfc_font_t *f;
+		gboolean is_subst = FALSE;
+		GList *l, *ll;
 
 		node = match->children;
 		while (node != NULL) {
 				if (xmlStrcmp(attr, (const xmlChar *)"lang") == 0) {
 					if (lang)
 						xmlFree(lang);
-					lang = _ezfc_config_parse_string_node(node->children);
+					l = _ezfc_config_parse_string_node(node->children);
+					/* multiple node is not well supported */
+					lang = xmlStrdup(l->data);
+					g_list_free_full(l, xmlFree);
 				} else if (xmlStrcmp(attr, (const xmlChar *)"family") == 0) {
 					if (alias)
 						xmlFree(alias);
-					alias = _ezfc_config_parse_string_node(node->children);
+					l = _ezfc_config_parse_string_node(node->children);
+					/* multiple node is not well supported */
+					alias = xmlStrdup(l->data);
+					g_list_free_full(l, xmlFree);
 				} else {
 					g_warning("Unexpected value in the name attribute on <test>: %s\n",
 						  attr);
 				if (xmlStrcmp(attr, (const xmlChar *)"append_last") == 0) {
 					/* kind of the alternatives of <default> */
 					/* We are assuming that there should be the <prefer> rule too.
-					 * so ignoring.
+					 * so ignoring except if binding is "same".
 					 */
 					xmlFree(attr);
-					goto bail2;
+					attr = xmlGetProp(node, (const xmlChar *)"binding");
+					if (attr && xmlStrcmp(attr, (const xmlChar *)"same"))
+					{
+						xmlFree(attr);
+						goto bail2;
+					}
+					/* deal with this as the substitute font definition */
+					xmlFree(attr);
+					attr = xmlGetProp(node, (const xmlChar *)"name");
+					if (xmlStrcmp(attr, (const xmlChar *)"family") == 0) {
+						l = _ezfc_config_parse_string_node(node->children);
+						for (ll = l; ll != NULL; ll = g_list_next(ll))
+							FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)ll->data);
+						g_list_free_full(l, xmlFree);
+					} else {
+						g_warning("Unexpected value in the name attribute: %s",
+							  attr);
+					}
+					is_subst = TRUE;
 				} else {
 					xmlFree(attr);
 					attr = xmlGetProp(node, (const xmlChar *)"name");
 					if (xmlStrcmp(attr, (const xmlChar *)"family") == 0) {
-						s = _ezfc_config_parse_string_node(node->children);
-						FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)s);
-						xmlFree(s);
+						l = _ezfc_config_parse_string_node(node->children);
+						for (ll = l; ll != NULL; ll = g_list_next(ll))
+							FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *)ll->data);
+						g_list_free_full(l, xmlFree);
 					} else {
 						g_warning("Unexpected value in the name attribute: %s",
 							  attr);
 			g_warning("No alias name is defined.");
 			goto bail2;
 		}
-		a = ezfc_alias_new((const gchar *)alias);
-		if (!ezfc_alias_set_font_pattern(a, pat, &err)) {
+		if (is_subst) {
+			f = ezfc_font_new();
+			if (!ezfc_font_set_pattern(f, pat, &err)) {
+				ezfc_font_unref(f);
+				goto bail2;
+			}
+			ezfc_config_add_subst(config, (const gchar *)alias, f);
+			ezfc_font_unref(f);
+		} else {
+			a = ezfc_alias_new((const gchar *)alias);
+			if (!ezfc_alias_set_font_pattern(a, pat, &err)) {
+				ezfc_alias_unref(a);
+				goto bail2;
+			}
+			ezfc_config_add_alias(config, (const gchar *)lang, a);
 			ezfc_alias_unref(a);
-			goto bail2;
 		}
-		ezfc_config_add_alias(config, (const gchar *)lang, a);
-		ezfc_alias_unref(a);
 	  bail2:
 		if (lang)
 			xmlFree(lang);
 	for (i = 0; i < n; i++) {
 		xmlNodePtr match = xmlXPathNodeSetItem(xobj->nodesetval, i);
 		xmlNodePtr node;
-		xmlChar *attr, *s;
+		xmlChar *attr;
 		ezfc_font_t *f = NULL;
+		GList *l;
 
 		node = match->children;
 		while (node != NULL) {
 				attr = xmlGetProp(node, (const xmlChar *)"name");
 				if (xmlStrcmp(attr, (const xmlChar *)"family") == 0) {
 					f = ezfc_font_new();
-					s = _ezfc_config_parse_string_node(node->children);
-					ezfc_font_set_family(f, (const gchar *)s, &err);
+					l = _ezfc_config_parse_string_node(node->children);
+					/* multiple values is not well supported */
+					ezfc_font_add_family(f, (const gchar *)l->data, &err);
+					g_list_free_full(l, xmlFree);
 				}
 				xmlFree(attr);
 			} else if (xmlStrcmp(node->name, (const xmlChar *)"edit") == 0) {
 						      g_str_equal,
 						      g_free,
 						      (GDestroyNotify)ezfc_font_unref);
+		retval->subst = g_hash_table_new_full(g_str_hash,
+						      g_str_equal,
+						      g_free,
+						      (GDestroyNotify)ezfc_container_unref);
 		retval->name = NULL;
 		retval->priority = 0;
 		retval->loaded = FALSE;
 		retval->migration = TRUE;
 		ezfc_mem_add_ref(&retval->parent, retval->aliases,
 				 (ezfc_destroy_func_t)g_hash_table_destroy);
+		ezfc_mem_add_ref(&retval->parent, retval->fonts,
+				 (ezfc_destroy_func_t)g_hash_table_destroy);
+		ezfc_mem_add_ref(&retval->parent, retval->subst,
+				 (ezfc_destroy_func_t)g_hash_table_destroy);
 	}
 
 	return retval;
 }
 
 /**
+ * ezfc_config_add_subst:
+ * @config: a #ezfc_config_t.
+ * @family_name: a family name to be substituted.
+ * @subst: a #ezfc_font_t for substitute font
+ *
+ * Add a #subst font as a substitute font for @family_name.
+ *
+ * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
+ *
+ * Since: 0.11
+ */
+gboolean
+ezfc_config_add_subst(ezfc_config_t *config,
+		      const gchar   *family_name,
+		      ezfc_font_t   *subst)
+{
+	ezfc_container_t *container;
+	GList *list, *l;
+
+	g_return_val_if_fail (config != NULL, FALSE);
+	g_return_val_if_fail (family_name != NULL && family_name[0], FALSE);
+	g_return_val_if_fail (subst != NULL, FALSE);
+
+	container = g_hash_table_lookup(config->subst, family_name);
+	if (!container) {
+		container = ezfc_container_new((ezfc_destroy_func_t)_ezfc_config_subst_list_free);
+		g_hash_table_insert(config->subst,
+				    g_strdup(family_name),
+				    container);
+	}
+	l = list = ezfc_container_get(container);
+	while (l != NULL) {
+		ezfc_font_t *f = l->data;
+
+		if (g_ascii_strcasecmp(ezfc_font_get_family(f),
+				       ezfc_font_get_family(subst)) == 0) {
+			l->data = ezfc_font_ref(subst);
+			ezfc_font_unref(f);
+			goto bail;
+		}
+		if (g_list_next(l) == NULL)
+			break;
+		l = g_list_next(l);
+	}
+	l = g_list_append(l, ezfc_font_ref(subst));
+  bail:
+	if (!list)
+		list = l;
+	ezfc_container_set(container, list);
+
+	return TRUE;
+}
+
+/**
+ * ezfc_config_remove_subst:
+ * @config: a #ezfc_config_t.
+ * @family_name: a family name to be substituted.
+ * @subst_name: a substitute font name
+ *
+ * Remove @subst_name from the substitute font list of @family_name.
+ *
+ * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
+ *
+ * Since: 0.11
+ */
+gboolean
+ezfc_config_remove_subst(ezfc_config_t *config,
+			 const gchar   *family_name,
+			 const gchar   *subst_name)
+{
+	ezfc_container_t *container;
+	GList *list, *l;
+	gboolean retval = TRUE;
+	GError *err = NULL;
+
+	g_return_val_if_fail (config != NULL, FALSE);
+	g_return_val_if_fail (family_name != NULL && family_name[0], FALSE);
+	g_return_val_if_fail (subst_name != NULL && subst_name[0], FALSE);
+
+	if ((container = g_hash_table_lookup(config->subst, family_name)) == NULL)
+		return FALSE;
+
+	list = ezfc_container_get(container);
+	for (l = list; l != NULL; l = g_list_next(l)) {
+		ezfc_font_t *f = l->data;
+
+		if (ezfc_font_remove_family(f, subst_name, &err)) {
+			GList *ll = ezfc_font_get_families(f);
+
+			if (!ll) {
+				if (g_list_length(list) == 1) {
+					g_hash_table_remove(config->subst, family_name);
+				} else {
+					if (l == list) {
+						list = g_list_delete_link(l, l);
+						ezfc_container_set(container, list);
+					} else {
+						l = g_list_delete_link(l, l);
+					}
+					ezfc_font_unref(f);
+				}
+			} else {
+				g_list_free(ll);
+			}
+			goto bail;
+		}
+	}
+	retval = FALSE;
+  bail:
+
+	return retval;
+}
+
+/**
+ * ezfc_config_remove_substs:
+ * @config: a #ezfc_config_t.
+ * @family_name: a family name to be substituted.
+ *
+ * Remove all of substitute font list of @family_name.
+ *
+ * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
+ *
+ * Since: 0.11
+ */
+gboolean
+ezfc_config_remove_substs(ezfc_config_t *config,
+			  const gchar   *family_name)
+{
+	gboolean retval;
+
+	g_return_val_if_fail (config != NULL, FALSE);
+	g_return_val_if_fail (family_name != NULL && family_name[0], FALSE);
+
+	retval = g_hash_table_remove(config->subst, family_name);
+
+	return retval;
+}
+
+/**
  * ezfc_config_get_language_list:
  * @config: a #ezfc_config_t.
  *
 }
 
 /**
+ * ezfc_config_get_subst_family:
+ * @config: a #ezfc_config_t.
+ *
+ * Obtains the list of the family name being substituted
+ *
+ * Returns: (element-type utf8) (transfer none): a #GList contains languages or %NULL.
+ *
+ * Since: 0.11
+ */
+GList *
+ezfc_config_get_subst_family(ezfc_config_t *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return g_hash_table_get_keys(config->subst);
+}
+
+/**
+ * ezfc_config_get_substs:
+ * @config: a #ezfc_config_t.
+ * @family_name: a family name being substituted.
+ *
+ * Obtains the list of #ezfc_font_t to be substituted for @family_name.
+ *
+ * Returns: (element-type ezfc_font_t) (transfer none): a #GList contains #ezfc_font_t or %NULL.
+ *
+ * Since: 0.11
+ */
+const GList *
+ezfc_config_get_substs(ezfc_config_t *config,
+		       const gchar   *family_name)
+{
+	ezfc_container_t *container;
+	const GList *retval;
+
+	g_return_val_if_fail (config != NULL, NULL);
+	g_return_val_if_fail (family_name != NULL && family_name[0], NULL);
+
+	container = g_hash_table_lookup(config->subst, family_name);
+	if (!container)
+		retval = NULL;
+	else
+		retval = ezfc_container_get(container);
+
+	return retval;
+}
+
+/**
  * ezfc_config_load:
  * @config: a #ezfc_config_t.
  * @error: (allow-none): a #GError.

libeasyfc/ezfc-config.h

 gboolean       ezfc_config_remove_font      (ezfc_config_t  *config,
                                              const gchar    *family);
 gboolean       ezfc_config_remove_fonts     (ezfc_config_t  *config);
+gboolean       ezfc_config_add_subst        (ezfc_config_t  *config,
+					     const gchar    *family_name,
+					     ezfc_font_t    *subst);
+gboolean       ezfc_config_remove_subst     (ezfc_config_t  *config,
+					     const gchar    *family_name,
+					     const gchar    *subst_name);
+gboolean       ezfc_config_remove_substs    (ezfc_config_t  *config,
+					     const gchar    *family_name);
 GList         *ezfc_config_get_language_list(ezfc_config_t  *config);
 const GList   *ezfc_config_get_aliases      (ezfc_config_t  *config,
                                              const gchar    *language);
 GList         *ezfc_config_get_fonts        (ezfc_config_t  *config);
+GList         *ezfc_config_get_subst_family (ezfc_config_t  *config);
+const GList   *ezfc_config_get_substs       (ezfc_config_t  *config,
+					     const gchar    *family_name);
 gboolean       ezfc_config_load             (ezfc_config_t  *config,
                                              GError        **error);
 gboolean       ezfc_config_save             (ezfc_config_t  *config,

libeasyfc/ezfc-font.c

  * ezfc_font_get_family:
  * @font: a #ezfc_font_t.
  *
- * Obtains the font family name in @font.
+ * Obtains the font family name in first place in @font.
  *
  * Returns: the font name.
  */
 }
 
 /**
+ * ezfc_font_get_families:
+ * @font: a #ezfc_font_t.
+ *
+ * Obtains font family names in @font.
+ *
+ * Returns: (transfer container) (element-type utf8): a #GList containing
+ *          the static string of font family names
+ *
+ * Since: 0.11
+ */
+GList *
+ezfc_font_get_families(ezfc_font_t *font)
+{
+	ezfc_font_private_t *priv = (ezfc_font_private_t *)font;
+	GList *retval = NULL;
+	FcChar8 *s;
+
+	g_return_val_if_fail (font != NULL, NULL);
+
+	if (priv->pattern) {
+		int i;
+
+		for (i = 0; ; i++) {
+			if (FcPatternGetString(priv->pattern,
+					       FC_FAMILY, i, &s) != FcResultMatch)
+				break;
+			retval = g_list_append(retval, s);
+		}
+	}
+
+	return retval;
+}
+
+/**
+ * ezfc_font_find:
+ * @font: a #ezfc_font_t.
+ * @font_name: a font name.
+ *
+ * Check if @font contains @font_name.
+ *
+ * Returns: %TRUE if it contains, otherwise %FALSE.
+ *
+ * Since: 0.11
+ */
+gboolean
+ezfc_font_find(ezfc_font_t *font,
+	       const gchar *font_name)
+{
+	ezfc_font_private_t *priv = (ezfc_font_private_t *)font;
+	gboolean retval = FALSE;
+	FcChar8 *s;
+	int i;
+
+	g_return_val_if_fail (font != NULL, FALSE);
+	g_return_val_if_fail (font_name != NULL, FALSE);
+
+	if (priv->pattern) {
+		for (i = 0; ; i++) {
+			if (FcPatternGetString(priv->pattern, FC_FAMILY, i, &s) != FcResultMatch)
+				break;
+			if (g_ascii_strcasecmp(font_name, (const gchar *)s) == 0) {
+				retval = TRUE;
+				break;
+			}
+		}
+	}
+
+	return retval;
+}
+
+/**
  * ezfc_font_set_family:
  * @font: a #ezfc_font_t.
  * @font_name: a font name.
  * Set @font_name as the font family name used for the font font.
  *
  * Returns: %TRUE if it successfully is set. otherwise %FALSE.
+ *
+ * Deprecated: 0.11. Use ezfc_font_add_family().
  */
 gboolean
 ezfc_font_set_family(ezfc_font_t   *font,
 		     const gchar   *font_name,
 		     GError       **error)
 {
+	return ezfc_font_add_family(font, font_name, error);
+}
+
+/**
+ * ezfc_font_add_family:
+ * @font: a #ezfc_font_t.
+ * @font_name: a font name.
+ * @error: (allow-none): a #GError.
+ *
+ * Add @font_name as the font family name used for the font font.
+ *
+ * Returns: %TRUE if it successfully is set. otherwise %FALSE.
+ *
+ * Since: 0.11
+ */
+gboolean
+ezfc_font_add_family(ezfc_font_t   *font,
+		     const gchar   *font_name,
+		     GError       **error)
+{
 	ezfc_font_private_t *priv = (ezfc_font_private_t *)font;
 	FcPattern *pat, *match = NULL;
 	FcResult result;
 }
 
 /**
+ * ezfc_font_remove:
+ * @font: a #ezfc_font_t.
+ * @error: (allow-none): a #GError.
+ *
+ * Removes all of families in @font.
+ *
+ * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
+ *
+ * Since: 0.11
+ */
+gboolean
+ezfc_font_remove(ezfc_font_t  *font,
+		 GError      **error)
+{
+	ezfc_font_private_t *priv = (ezfc_font_private_t *)font;
+	gboolean retval = FALSE;
+	GError *err = NULL;
+
+	g_return_val_if_fail (font != NULL, FALSE);
+
+	if (priv->pattern) {
+		if (FcPatternDel(priv->pattern, FC_FAMILY)) {
+			retval = TRUE;
+		} else {
+			g_set_error(&err, EZFC_ERROR, EZFC_ERR_NO_FAMILY,
+				    "Pattern doesn't contain any family name");
+		}
+	}
+	if (err) {
+		if (error)
+			*error = g_error_copy(err);
+		else
+			g_warning(err->message);
+		g_error_free(err);
+	}
+
+	return retval;
+}
+
+/**
+ * ezfc_font_remove_family:
+ * @font: a #ezfc_font_t.
+ * @font_name: a font name to be removed.
+ * @error: (allow-none): a #GError.
+ *
+ * Removes @font_name from @font.
+ *
+ * Returns: %TRUE if it's successfully completed, otherwise %FALSE.
+ *
+ * Since: 0.11
+ */
+gboolean
+ezfc_font_remove_family(ezfc_font_t  *font,
+			const gchar  *font_name,
+			GError      **error)
+{
+	ezfc_font_private_t *priv = (ezfc_font_private_t *)font;
+	gboolean retval = FALSE;
+	GError *err = NULL;
+	int i;
+	FcChar8 *s;
+
+	g_return_val_if_fail (font != NULL, FALSE);
+	g_return_val_if_fail (font_name != NULL, FALSE);
+
+	if (priv->pattern) {
+		for (i = 0; ; i++) {
+			if (FcPatternGetString(priv->pattern, FC_FAMILY, i, &s) != FcResultMatch)
+				break;
+			if (g_ascii_strcasecmp(font_name, (const gchar *)s) == 0) {
+				if (FcPatternRemove(priv->pattern, FC_FAMILY, i)) {
+					retval = TRUE;
+				} else {
+					g_set_error(&err, EZFC_ERROR, EZFC_ERR_FAIL_ON_FC,
+						    "Unable to remove a family: %s",
+						    font_name);
+				}
+				break;
+			}
+		}
+	}
+
+	return retval;
+}
+
+/**
  * ezfc_font_check_existence:
  * @font: a #ezfc_font_t.
  * @flag: a boolean value.

libeasyfc/ezfc-font.h

 
 #include <glib.h>
 #include <fontconfig/fontconfig.h>
+#include <libeasyfc/ezfc-utils.h>
 
 G_BEGIN_DECLS
 
                                                                    const FcPattern              *pattern,
                                                                    GError                      **error);
 const gchar                 *ezfc_font_get_family                 (ezfc_font_t                  *font);
-gboolean                     ezfc_font_set_family                 (ezfc_font_t                  *font,
+GList                       *ezfc_font_get_families               (ezfc_font_t                  *font);
+gboolean                     ezfc_font_find                       (ezfc_font_t                  *font,
+								   const gchar                  *font_name);
+gboolean                     ezfc_font_add_family                 (ezfc_font_t                  *font,
                                                                    const gchar                  *font_name,
                                                                    GError                      **error);
+gboolean                     ezfc_font_remove                     (ezfc_font_t                  *font,
+								   GError                      **error);
+gboolean                     ezfc_font_remove_family              (ezfc_font_t                  *font,
+								   const gchar                  *font_name,
+								   GError                      **error);
 void                         ezfc_font_check_existence            (ezfc_font_t                  *font,
                                                                    gboolean                      flag);
 void                         ezfc_font_set_hinting                (ezfc_font_t                  *font,
                                                                    ezfc_font_subpixel_render_t   mode);
 ezfc_font_subpixel_render_t  ezfc_font_get_subpixel_rendering     (ezfc_font_t                  *font);
 
+#ifndef EZFC_DISABLE_DEPRECATED
+EZFC_DEPRECATED_FOR(ezfc_font_add_family)
+gboolean                     ezfc_font_set_family                 (ezfc_font_t                  *font,
+                                                                   const gchar                  *font_name,
+                                                                   GError                      **error);
+#endif /* !EZFC_DISABLE_DEPRECATED */
+
 G_END_DECLS
 
 #endif /* __EZFC_FONT_H__ */

tests/ezfc-tool.py

 config_extra_name = None
 
 def __cmd_add_cb(argv):
-    opts, args = getopt.getopt(argv, 'a:hl:',
-                               ['alias=', 'help', 'lang=', 'no-load',
+    opts, args = getopt.getopt(argv, 'a:hl:s:',
+                               ['alias=', 'help', 'lang=', 'subst=', 'no-load',
                                 'hinting=', 'autohint=', 'antialias=',
                                 'embeddedbitmap=', 'rgba=', 'hintstyle='])
     opts.append(('', None)) # just to ensure entering the loop
         if o in ('-h', '--help') or len(args) != 1:
             print 'Usage: %s add [options] <family>' % os.path.basename(sys.argv[0])
             print 'Options:'
-            print '  -a or --alias=ALIAS      Set ALIAS as the alias font for family'
-            print '  -h or --help             Show this message'
-            print '  -l or --lang=LANG        Set LANG as the language to be added'
-            print '  --no-load                Do not load the configuration file'
-            print '  --hinting=<bool>         Set a boolean value for hinting'
-            print '  --autohint=<bool>        Set a boolean value for auto-hinting'
-            print '  --antialias=<bool>       Set a boolean value for antialiasing'
-            print '  --embeddedbitmap=<bool>  Set a boolean value for embeddedbitmap'
-            print '  --rgba=<const>           Set rgba'
-            print '  --hintstyle=<const>      Set hintstyle'
+            print '  -a or --alias=ALIAS        Set ALIAS as the alias font for family'
+            print '  -h or --help               Show this message'
+            print '  -l or --lang=LANG          Set LANG as the language to be added'
+            print '  -s or --subst=SUBST[,...]  Set SUBST as the substitute fonts for family'
+            print '  --no-load                  Do not load the configuration file'
+            print '  --hinting=<bool>           Set a boolean value for hinting'
+            print '  --autohint=<bool>          Set a boolean value for auto-hinting'
+            print '  --antialias=<bool>         Set a boolean value for antialiasing'
+            print '  --embeddedbitmap=<bool>    Set a boolean value for embeddedbitmap'
+            print '  --rgba=<const>             Set rgba'
+            print '  --hintstyle=<const>        Set hintstyle'
             sys.exit()
         elif o in ('-a', '--alias'):
             alias = a
         elif o in ('-l', '--lang'):
             lang = a
+        elif o in ('-s', '--subst'):
+            subst = a
         elif o == '--no-load':
             loadconf = False
         elif o == '--hinting':
         eza.set_font(args[0])
         config.add_alias(lang, eza)
         msg = '%s has been added as the alias of %s for %s' % (args[0], alias, lang)
+    elif subst != None:
+        for n in subst.split(','):
+            ezf = Easyfc.Font()
+            ezf.set_family(n)
+            config.add_subst(args[0], ezf)
+        msg = '%s has been added as the subst of %s' % (subst, args[0])
     else:
         ezf = Easyfc.Font()
         ezf.set_family(args[0])
     print msg
 
 def __cmd_remove_cb(argv):
-    opts, args = getopt.getopt(argv, 'h', ['help'])
+    opts, args = getopt.getopt(argv, 'ahl:ps:', ['alias', 'help', 'lang=', 'prop', 'subst='])
     opts.append(('', None)) # just to ensure entering the loop
+    mode = None
+    lang = None
+    subst = None
     for o, a in opts:
-        if o in ('-h', '--help') or len(args) != 2:
-            print 'Usage: %s remove [options] <lang> <alias>' % os.path.basename(sys.argv[0])
+        if o in ('-h', '--help') or len(args) != 1:
+            print 'Usage: %s remove [options] <family>' % os.path.basename(sys.argv[0])
             print 'Options:'
-            print '  -h or --help    Show this message'
+            print '  -a or --alias        Remove the alias'
+            print '  -p or --prop         Remove the font properties'
+            print '  -s or --subst=SUBST  Remove SUBST from the substitute font'
+            print '  -l or --lang=LANG    Set LANG as the language to be removed. imply -a option'
+            print '  -h or --help         Show this message'
             sys.exit()
+        elif o in ('-a', '--alias'):
+            mode = 'alias'
+        elif o in ('-p', '--prop'):
+            mode = 'prop'
+        elif o in ('-s', '--subst'):
+            mode = 'subst'
+            subst = a
+        elif o in ('-l', '--lang'):
+            if mode != 'alias':
+                print "E: --lang has to be set with --alias option"
+                sys.exit()
+            else:
+                lang = a
 
     config = Easyfc.Config()
     config.set_priority(config_priority)
         else:
             raise
 
-    config.remove_alias(args[0], args[1])
+    if mode == 'alias':
+        if not config.remove_alias(lang, args[0]):
+            msg = 'Unable to remove %s from %s' % (args[0], lang)
+        else:
+            msg = '%s has been removed from %s' % (args[0], lang)
+    elif mode == 'prop':
+        if not config.remove_font(args[0]):
+            msg = 'Unable to remove the properties for %s' % args[0]
+        else:
+            msg = 'Properties has been removed for %s' % args[0]
+    elif mode == 'subst':
+        if not config.remove_subst(args[0], subst):
+            msg = 'Unable to remove %s from %s' % (subst, args[0])
+        else:
+            msg = '%s has been removed from %s' % (subst, args[0])
+    else:
+        print "E: Unexpected operation mode: %s" % mode
+        sys.exit()
     config.save()
-    print '%s has been removed from %s' % (args[1], args[0])
+    print msg
 
 def __cmd_show_cb(argv):
     opts, args = getopt.getopt(argv, 'h', ['help'])
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.